diff --git a/ports/src/noux/child.h b/ports/src/noux/child.h index 4eb7450c5..34c851594 100644 --- a/ports/src/noux/child.h +++ b/ports/src/noux/child.h @@ -16,8 +16,6 @@ /* Genode includes */ #include -#include -#include #include #include #include @@ -30,6 +28,10 @@ #include #include #include +#include +#include +#include + namespace Noux { @@ -61,11 +63,7 @@ namespace Noux { /** * Return singleton instance of PID allocator */ - Pid_allocator *pid_allocator() - { - static Pid_allocator inst; - return &inst; - } + Pid_allocator *pid_allocator(); class Child; @@ -155,598 +153,23 @@ namespace Noux { */ struct Resources { - /* - * XXX not used yet + /** + * Entrypoint used to serve the RPC interfaces of the + * locally-provided services */ - struct Dataspace_destroyer - { - virtual void destroy(Dataspace_capability) = 0; - }; - - - class Dataspace_registry; - - - class Dataspace_info : public Object_pool::Entry - { - private: - - size_t _size; - Dataspace_capability _ds_cap; - Dataspace_destroyer &_destroyer; - - public: - - Dataspace_info(Dataspace_capability ds_cap, - Dataspace_destroyer &destroyer) - : - Object_pool::Entry(ds_cap), - _size(Dataspace_client(ds_cap).size()), - _ds_cap(ds_cap), - _destroyer(destroyer) - { } - - size_t size() const { return _size; } - Dataspace_capability ds_cap() const { return _ds_cap; } - - /** - * Create shadow copy of dataspace - * - * \param ds_registry registry for keeping track of - * the new dataspace - * \param ep entrypoint used to serve the RPC - * interface of the new dataspace - * (used if the dataspace is a sub - * RM session) - * \return capability for the new dataspace - */ - virtual Dataspace_capability fork(Ram_session_capability ram, - Dataspace_destroyer &destroyer, - Dataspace_registry &ds_registry, - Rpc_entrypoint &ep) = 0; - - /** - * Write data sequence into dataspace - * - * \param dst_offset destination offset within dataspace - * \param src data source buffer - * \param len length of source buffer in bytes - */ - virtual void poke(addr_t dst_offset, void const *src, size_t len) = 0; - - Dataspace_destroyer &destroyer() { return _destroyer; } - }; - - - class Dataspace_registry - { - private: - - Object_pool _pool; - - public: - - void insert(Dataspace_info *info) - { - _pool.insert(info); - } - - void remove(Dataspace_info *info) - { - _pool.remove(info); - } - - Dataspace_info *lookup_info(Dataspace_capability ds_cap) - { - return _pool.obj_by_cap(ds_cap); - } - }; - - - struct Ram_dataspace_info : Dataspace_info, - List::Element - { - Ram_dataspace_info(Ram_dataspace_capability ds_cap, - Dataspace_destroyer &destroyer) - : Dataspace_info(ds_cap, destroyer) { } - - Dataspace_capability fork(Ram_session_capability ram, - Dataspace_destroyer &destroyer, - Dataspace_registry &, - Rpc_entrypoint &) - { - size_t const size = Dataspace_client(ds_cap()).size(); - - Ram_dataspace_capability dst_ds; - - try { - dst_ds = Ram_session_client(ram).alloc(size); - } catch (...) { - return Dataspace_capability(); - } - - void *src = 0; - try { - src = Genode::env()->rm_session()->attach(ds_cap()); - } catch (...) { } - - void *dst = 0; - try { - dst = Genode::env()->rm_session()->attach(dst_ds); - } catch (...) { } - - if (src && dst) - memcpy(dst, src, size); - - if (src) Genode::env()->rm_session()->detach(src); - if (dst) Genode::env()->rm_session()->detach(dst); - - if (!src || !dst) { - Ram_session_client(ram).free(dst_ds); - return Dataspace_capability(); - } - - return dst_ds; - } - - void poke(addr_t dst_offset, void const *src, size_t len) - { - if ((dst_offset >= size()) || (dst_offset + len > size())) { - PERR("illegal attemt to write beyond dataspace boundary"); - return; - } - - char *dst = 0; - try { - dst = Genode::env()->rm_session()->attach(ds_cap()); - } catch (...) { } - - if (src && dst) - memcpy(dst + dst_offset, src, len); - - if (dst) Genode::env()->rm_session()->detach(dst); - } - }; - - - struct Local_ram_session : Rpc_object, - Dataspace_destroyer - { - List _list; - - /* - * Track the RAM resources accumulated via RAM session - * allocations. - */ - size_t _used_quota; - - Dataspace_registry &_registry; - - /** - * Constructor - */ - Local_ram_session(Dataspace_registry ®istry) - : _used_quota(0), _registry(registry) { } - - /** - * Destructor - */ - ~Local_ram_session() - { - Ram_dataspace_info *info = 0; - while ((info = _list.first())) - free(static_cap_cast(info->ds_cap())); - } - - - /*********************************** - ** Dataspace_destroyer interface ** - ***********************************/ - - /* XXX not used yet */ - void destroy(Dataspace_capability ds) - { - free(static_cap_cast(ds)); - } - - - /*************************** - ** Ram_session interface ** - ***************************/ - - Ram_dataspace_capability alloc(size_t size) - { - Ram_dataspace_capability ds_cap = env()->ram_session()->alloc(size); - - Ram_dataspace_info *ds_info = new (env()->heap()) - Ram_dataspace_info(ds_cap, *this); - - _used_quota += ds_info->size(); - - _registry.insert(ds_info); - _list.insert(ds_info); - - return ds_cap; - } - - void free(Ram_dataspace_capability ds_cap) - { - Ram_dataspace_info *ds_info = - dynamic_cast(_registry.lookup_info(ds_cap)); - - if (!ds_info) { - PERR("RAM free: dataspace lookup failed"); - return; - } - - _registry.remove(ds_info); - _list.remove(ds_info); - _used_quota -= ds_info->size(); - - env()->ram_session()->free(ds_cap); - Genode::destroy(env()->heap(), ds_info); - } - - int ref_account(Ram_session_capability) { return 0; } - int transfer_quota(Ram_session_capability, size_t) { return 0; } - size_t quota() { return env()->ram_session()->quota(); } - size_t used() { return _used_quota; } - }; - + Rpc_entrypoint &ep; /** - * Used to record all RM attachements + * Registry of dataspaces owned by the Noux process */ - struct Local_rm_session : Rpc_object - { - struct Region : List::Element - { - Dataspace_capability ds; - size_t size; - off_t offset; - addr_t local_addr; - - Region(Dataspace_capability ds, size_t size, - off_t offset, addr_t local_addr) - : - ds(ds), size(size), - offset(offset), local_addr(local_addr) - { } - - /** - * Return true if region contains specified address - */ - bool contains(addr_t addr) const - { - return (addr >= local_addr) - && (addr < local_addr + size); - } - }; - - Rm_connection _rm; - - Dataspace_registry &_ds_registry; - - List _regions; - - Region *_lookup_region_by_addr(addr_t local_addr) - { - Region *curr = _regions.first(); - for (; curr; curr = curr->next()) { - if (curr->contains(local_addr)) - return curr; - } - return 0; - } - - Local_rm_session(Dataspace_registry &ds_registry, - addr_t start = ~0UL, size_t size = 0) - : _rm(start, size), _ds_registry(ds_registry) { } - - ~Local_rm_session() - { - Region *curr = 0; - for (; curr; curr = curr->next()) - detach(curr->local_addr); - } - - /** - * Replay attachments onto specified RM session - * - * \param dst_ram backing store used for allocating the - * the copies of RAM dataspaces - */ - void replay(Ram_session_capability dst_ram, - Rm_session_capability dst_rm, - Dataspace_registry &ds_registry, - Rpc_entrypoint &ep) - { - /* - * XXX remove this - */ - struct Dummy_destroyer : Dataspace_destroyer - { - void destroy(Dataspace_capability) { } - }; - static Dummy_destroyer dummy_destroyer; - - Region *curr = _regions.first(); - for (; curr; curr = curr->next()) { - - Dataspace_capability ds; - - Dataspace_info *info = _ds_registry.lookup_info(curr->ds); - - if (info) { - /* - * XXX using 'this' as destroyer may be false, - * the destroyer should better correspond to - * dst_ram. - */ - ds = info->fork(dst_ram, - dummy_destroyer, - ds_registry, - ep); - if (!ds.valid()) - PERR("replay: Error while forking dataspace"); - - /* - * XXX We could detect dataspaces that are - * attached more than once. For now, we - * create a new fork for each attachment. - */ - - } else { - - /* - * If the dataspace is not a RAM dataspace, - * assume that it's a ROM dataspace. - * - * XXX Handle ROM dataspaces explicitly. For - * once, we need to make sure that they - * remain available until the child process - * exits even if the parent process exits - * earlier. Furthermore, we would like to - * detect unexpected dataspaces. - */ - ds = curr->ds; - PWRN("replay: unknown dataspace type, assume ROM"); - } - - Rm_session_client(dst_rm).attach(ds, curr->size, - curr->offset, - true, - curr->local_addr); - } - } - - void poke(addr_t dst_addr, void const *src, size_t len) - { - Region *region = _lookup_region_by_addr(dst_addr); - if (!region) { - PERR("poke: no region at 0x%lx", dst_addr); - return; - } - - /* - * Test if start and end address occupied by the object - * type refers to the same region. - */ - if (region != _lookup_region_by_addr(dst_addr + len - 1)) { - PERR("attempt to write beyond region boundary"); - return; - } - - Dataspace_info *info = _ds_registry.lookup_info(region->ds); - if (!info) { - PERR("attempt to write to unknown dataspace type"); - for (;;); - return; - } - - if (region->offset) { - PERR("poke: writing to region with offset is not supported"); - return; - } - - info->poke(dst_addr - region->local_addr, src, len); - } - - - /************************** - ** RM session interface ** - **************************/ - - Local_addr attach(Dataspace_capability ds, - size_t size = 0, off_t offset = 0, - bool use_local_addr = false, - Local_addr local_addr = (addr_t)0) - { - if (size == 0) - size = Dataspace_client(ds).size(); - - /* - * 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); - - /* - * Record attachement for later replay (needed during - * fork) - */ - _regions.insert(new (Genode::env()->heap()) - Region(ds, size, offset, local_addr)); - return local_addr; - } - - void detach(Local_addr local_addr) - { - _rm.detach(local_addr); - - Region *region = _lookup_region_by_addr(local_addr); - if (!region) { - PWRN("Attempt to detach unknown region at 0x%p", - (void *)local_addr); - return; - } - - _regions.remove(region); - destroy(Genode::env()->heap(), region); - } - - Pager_capability add_client(Thread_capability thread) - { - return _rm.add_client(thread); - } - - void fault_handler(Signal_context_capability handler) - { - return _rm.fault_handler(handler); - } - - State state() - { - return _rm.state(); - } - - Dataspace_capability dataspace() - { - return _rm.dataspace(); - } - }; - - - struct Sub_rm_dataspace_info : Dataspace_info - { - struct Dummy_destroyer : Dataspace_destroyer - { - void destroy(Dataspace_capability) { } - } _dummy_destroyer; - - Local_rm_session &_sub_rm; - - Sub_rm_dataspace_info(Local_rm_session &sub_rm) - : - Dataspace_info(sub_rm.dataspace(), _dummy_destroyer), - _sub_rm(sub_rm) - { } - - Dataspace_capability fork(Ram_session_capability ram, - Dataspace_destroyer &destoyer, - Dataspace_registry &ds_registry, - Rpc_entrypoint &ep) - { - Local_rm_session *new_sub_rm = - new Local_rm_session(ds_registry, 0, size()); - - Rm_session_capability rm_cap = ep.manage(new_sub_rm); - - _sub_rm.replay(ram, rm_cap, ds_registry, ep); - - ds_registry.insert(new Sub_rm_dataspace_info(*new_sub_rm)); - - return new_sub_rm->dataspace(); - } - - void poke(addr_t dst_offset, void const *src, size_t len) - { - if ((dst_offset >= size()) || (dst_offset + len > size())) { - PERR("illegal attemt to write beyond RM boundary"); - return; - } - _sub_rm.poke(dst_offset, src, len); - } - }; - + Dataspace_registry ds_registry; /** - * Used to defer the execution of the process' main thread + * Locally-provided services for accessing platform resources */ - struct Local_cpu_session : Rpc_object - { - bool const forked; - Cpu_connection cpu; - Thread_capability main_thread; - - Local_cpu_session(char const *label, bool forked) - : forked(forked), cpu(label) { } - - Thread_capability create_thread(Name const &name) - { - /* - * Prevent any attempt to create more than the main - * thread. - */ - if (main_thread.valid()) { - PWRN("Invalid attempt to create a thread besides main"); - while (1); - return Thread_capability(); - } - main_thread = cpu.create_thread(name); - - PINF("created main thread"); - return main_thread; - } - - void kill_thread(Thread_capability thread) { - cpu.kill_thread(thread); } - - Thread_capability first() { - return cpu.first(); } - - Thread_capability next(Thread_capability curr) { - return cpu.next(curr); } - - int set_pager(Thread_capability thread, - Pager_capability pager) { - return cpu.set_pager(thread, pager); } - - int start(Thread_capability thread, addr_t ip, addr_t sp) - { - if (forked) { - PINF("defer attempt to start thread at ip 0x%lx", ip); - return 0; - } - return cpu.start(thread, ip, sp); - } - - void pause(Thread_capability thread) { - cpu.pause(thread); } - - void resume(Thread_capability thread) { - cpu.resume(thread); } - - void cancel_blocking(Thread_capability thread) { - cpu.cancel_blocking(thread); } - - int state(Thread_capability thread, Thread_state *dst) { - return cpu.state(thread, dst); } - - void exception_handler(Thread_capability thread, - Signal_context_capability handler) { - cpu.exception_handler(thread, handler); } - - void single_step(Thread_capability thread, bool enable) { - cpu.single_step(thread, enable); } - - /** - * Explicitly start main thread, only meaningful when - * 'forked' is true - */ - void start_main_thread(addr_t ip, addr_t sp) - { - cpu.start(main_thread, ip, sp); - } - }; - - Rpc_entrypoint &ep; - Dataspace_registry ds_registry; - Local_ram_session ram; - Local_cpu_session cpu; - Local_rm_session rm; + Ram_session_component ram; + Cpu_session_component cpu; + Rm_session_component rm; Resources(char const *label, Rpc_entrypoint &ep, bool forked) : @@ -818,41 +241,7 @@ namespace Noux { } _local_noux_service; - struct Local_sub_rm_service : public Service - { - Rpc_entrypoint &_ep; - Resources::Dataspace_registry &_ds_registry; - - Local_sub_rm_service(Rpc_entrypoint &ep, - Resources::Dataspace_registry &ds_registry) - : - Service(Rm_session::service_name()), - _ep(ep), _ds_registry(ds_registry) - { } - - Genode::Session_capability session(const char *args) - { - addr_t start = Arg_string::find_arg(args, "start").ulong_value(~0UL); - size_t size = Arg_string::find_arg(args, "size").ulong_value(0); - - Resources::Local_rm_session *rm = - new Resources::Local_rm_session(_ds_registry, start, size); - - Genode::Session_capability cap = _ep.manage(rm); - - _ds_registry.insert(new Resources::Sub_rm_dataspace_info(*rm)); - - return cap; - } - - void upgrade(Genode::Session_capability, const char *args) { } - - void close(Genode::Session_capability) - { - PWRN("Local_sub_rm_service::close not implemented, leaking memory"); - } - - } _local_sub_rm_service; + Local_rm_service _local_rm_service; /** * Exception type for failed file-descriptor lookup @@ -891,16 +280,16 @@ namespace Noux { * or children created via execve, or * true if the child is a fork from another child */ - Child(char const *name, - int pid, - Signal_receiver *sig_rec, - Vfs *vfs, - Args const &args, - char const *env, - Cap_session *cap_session, - Service_registry *parent_services, - Rpc_entrypoint &resources_ep, - bool forked) + Child(char const *name, + int pid, + Signal_receiver *sig_rec, + Vfs *vfs, + Args const &args, + char const *env, + Cap_session *cap_session, + Service_registry *parent_services, + Rpc_entrypoint &resources_ep, + bool forked) : _pid(pid), _sig_rec(sig_rec), @@ -927,7 +316,7 @@ namespace Noux { _sysio(_sysio_ds.local_addr()), _noux_session_cap(Session_capability(_entrypoint.manage(this))), _local_noux_service(_noux_session_cap), - _local_sub_rm_service(_entrypoint, _resources.ds_registry) + _local_rm_service(_entrypoint, _resources.ds_registry) { _args.dump(); strncpy(_name, name, sizeof(_name)); @@ -954,8 +343,7 @@ namespace Noux { Ram_session_capability ram() const { return _resources.ram.cap(); } Rm_session_capability rm() const { return _resources.rm.cap(); } - - Resources::Dataspace_registry &ds_registry() { return _resources.ds_registry; } + Dataspace_registry &ds_registry() { return _resources.ds_registry; } /**************************** @@ -985,7 +373,7 @@ namespace Noux { * space. */ if (strcmp(service_name, Rm_session::service_name()) == 0) - return &_local_sub_rm_service; + return &_local_rm_service; return _parent_services->find(service_name); } diff --git a/ports/src/noux/cpu_session_component.h b/ports/src/noux/cpu_session_component.h new file mode 100644 index 000000000..b9300d096 --- /dev/null +++ b/ports/src/noux/cpu_session_component.h @@ -0,0 +1,134 @@ +/* + * \brief CPU session provided to Noux processes + * \author Norman Feske + * \date 2012-02-22 + * + * The custom implementation of the CPU session interface is used to tweak + * the startup procedure as performed by the 'Process' class. Normally, + * processes start execution immediately at creation time at the ELF entry + * point. For implementing fork semantics, however, this default behavior + * does not work. Instead, we need to defer the start of the main thread + * until we have finished copying the address space of the forking process. + * Furthermore, we need to start the main thread at a custom trampoline + * function rather than at the ELF entry point. Those customizations are + * possible by wrapping core's CPU service. + */ + +/* + * Copyright (C) 2012 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__CPU_SESSION_COMPONENT_H_ +#define _NOUX__CPU_SESSION_COMPONENT_H_ + +/* Genode includes */ +#include +#include + +namespace Noux { + + using namespace Genode; + + class Cpu_session_component : public Rpc_object + { + private: + + bool const _forked; + Cpu_connection _cpu; + Thread_capability _main_thread; + + public: + + /** + * Constructor + * + * \param forked false if the CPU session belongs to a child + * created via execve or to the init process, or + * true if the CPU session belongs to a newly forked + * process. + * + * The 'forked' parameter controls the policy applied to the + * startup of the main thread. + */ + Cpu_session_component(char const *label, bool forked) + : _forked(forked), _cpu(label) { } + + /** + * Explicitly start main thread, only meaningful when + * 'forked' is true + */ + void start_main_thread(addr_t ip, addr_t sp) + { + _cpu.start(_main_thread, ip, sp); + } + + + /*************************** + ** Cpu_session interface ** + ***************************/ + + Thread_capability create_thread(Name const &name) + { + /* + * Prevent any attempt to create more than the main + * thread. + */ + if (_main_thread.valid()) { + PWRN("Invalid attempt to create a thread besides main"); + while (1); + return Thread_capability(); + } + _main_thread = _cpu.create_thread(name); + + PINF("created main thread"); + return _main_thread; + } + + void kill_thread(Thread_capability thread) { + _cpu.kill_thread(thread); } + + Thread_capability first() { + return _cpu.first(); } + + Thread_capability next(Thread_capability curr) { + return _cpu.next(curr); } + + int set_pager(Thread_capability thread, + Pager_capability pager) { + return _cpu.set_pager(thread, pager); } + + int start(Thread_capability thread, addr_t ip, addr_t sp) + { + if (_forked) { + PINF("defer attempt to start thread at ip 0x%lx", ip); + return 0; + } + return _cpu.start(thread, ip, sp); + } + + void pause(Thread_capability thread) { + _cpu.pause(thread); } + + void resume(Thread_capability thread) { + _cpu.resume(thread); } + + void cancel_blocking(Thread_capability thread) { + _cpu.cancel_blocking(thread); } + + int state(Thread_capability thread, Thread_state *dst) { + return _cpu.state(thread, dst); } + + void exception_handler(Thread_capability thread, + Signal_context_capability handler) { + _cpu.exception_handler(thread, handler); } + + void single_step(Thread_capability thread, bool enable) { + _cpu.single_step(thread, enable); } + + }; +} + +#endif /* _NOUX__CPU_SESSION_COMPONENT_H_ */ diff --git a/ports/src/noux/dataspace_registry.h b/ports/src/noux/dataspace_registry.h new file mode 100644 index 000000000..896c3e6bf --- /dev/null +++ b/ports/src/noux/dataspace_registry.h @@ -0,0 +1,112 @@ +/* + * \brief Registry for dataspaces used by noux processes + * \author Norman Feske + * \date 2012-02-22 + */ + +/* + * Copyright (C) 2012 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__DATASPACE_REGISTRY_H_ +#define _NOUX__DATASPACE_REGISTRY_H_ + +/* Genode includes */ +#include + +namespace Noux { + + using namespace Genode; + + /* + * XXX not used yet + */ + struct Dataspace_destroyer + { + virtual void destroy(Dataspace_capability) = 0; + }; + + + class Dataspace_registry; + + + class Dataspace_info : public Object_pool::Entry + { + private: + + size_t _size; + Dataspace_capability _ds_cap; + Dataspace_destroyer &_destroyer; + + public: + + Dataspace_info(Dataspace_capability ds_cap, + Dataspace_destroyer &destroyer) + : + Object_pool::Entry(ds_cap), + _size(Dataspace_client(ds_cap).size()), + _ds_cap(ds_cap), + _destroyer(destroyer) + { } + + size_t size() const { return _size; } + Dataspace_capability ds_cap() const { return _ds_cap; } + + /** + * Create shadow copy of dataspace + * + * \param ds_registry registry for keeping track of + * the new dataspace + * \param ep entrypoint used to serve the RPC + * interface of the new dataspace + * (used if the dataspace is a sub + * RM session) + * \return capability for the new dataspace + */ + virtual Dataspace_capability fork(Ram_session_capability ram, + Dataspace_destroyer &destroyer, + Dataspace_registry &ds_registry, + Rpc_entrypoint &ep) = 0; + + /** + * Write raw byte sequence into dataspace + * + * \param dst_offset destination offset within dataspace + * \param src data source buffer + * \param len length of source buffer in bytes + */ + virtual void poke(addr_t dst_offset, void const *src, size_t len) = 0; + + Dataspace_destroyer &destroyer() { return _destroyer; } + }; + + + class Dataspace_registry + { + private: + + Object_pool _pool; + + public: + + void insert(Dataspace_info *info) + { + _pool.insert(info); + } + + void remove(Dataspace_info *info) + { + _pool.remove(info); + } + + Dataspace_info *lookup_info(Dataspace_capability ds_cap) + { + return _pool.obj_by_cap(ds_cap); + } + }; +} + +#endif /* _NOUX__DATASPACE_REGISTRY_H_ */ diff --git a/ports/src/noux/local_rm_service.h b/ports/src/noux/local_rm_service.h new file mode 100644 index 000000000..997bf806e --- /dev/null +++ b/ports/src/noux/local_rm_service.h @@ -0,0 +1,119 @@ +/* + * \brief RM service provided to Noux processes + * \author Norman Feske + * \date 2012-02-22 + */ + +/* + * Copyright (C) 2012 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_RM_SERVICE_H_ +#define _NOUX__LOCAL_RM_SERVICE_H_ + +/* Genode includes */ +#include + +/* Noux includes */ +#include +#include + +namespace Noux { + + using namespace Genode; + + + class Rm_dataspace_info : public Dataspace_info + { + private: + + struct Dummy_destroyer : Dataspace_destroyer + { + void destroy(Dataspace_capability) { } + } _dummy_destroyer; + + Rm_session_component &_sub_rm; + + public: + + Rm_dataspace_info(Rm_session_component &sub_rm) + : + Dataspace_info(sub_rm.dataspace(), _dummy_destroyer), + _sub_rm(sub_rm) + { } + + Dataspace_capability fork(Ram_session_capability ram, + Dataspace_destroyer &, + Dataspace_registry &ds_registry, + Rpc_entrypoint &ep) + { + Rm_session_component *new_sub_rm = + new Rm_session_component(ds_registry, 0, size()); + + Rm_session_capability rm_cap = ep.manage(new_sub_rm); + + /* + * XXX Where to dissolve the RM session? + */ + + _sub_rm.replay(ram, rm_cap, ds_registry, ep); + + ds_registry.insert(new Rm_dataspace_info(*new_sub_rm)); + + return new_sub_rm->dataspace(); + } + + void poke(addr_t dst_offset, void const *src, size_t len) + { + if ((dst_offset >= size()) || (dst_offset + len > size())) { + PERR("illegal attemt to write beyond RM boundary"); + return; + } + _sub_rm.poke(dst_offset, src, len); + } + }; + + + class Local_rm_service : public Service + { + private: + + Rpc_entrypoint &_ep; + Dataspace_registry &_ds_registry; + + public: + + Local_rm_service(Rpc_entrypoint &ep, Dataspace_registry &ds_registry) + : + Service(Rm_session::service_name()), _ep(ep), + _ds_registry(ds_registry) + { } + + Genode::Session_capability session(const char *args) + { + addr_t start = Arg_string::find_arg(args, "start").ulong_value(~0UL); + size_t size = Arg_string::find_arg(args, "size").ulong_value(0); + + Rm_session_component *rm = + new Rm_session_component(_ds_registry, start, size); + + Genode::Session_capability cap = _ep.manage(rm); + + _ds_registry.insert(new Rm_dataspace_info(*rm)); + + return cap; + } + + void upgrade(Genode::Session_capability, const char *args) { } + + void close(Genode::Session_capability) + { + PWRN("Local_rm_service::close not implemented, leaking memory"); + } + }; +} + +#endif /* _NOUX__LOCAL_RM_SERVICE_H_ */ diff --git a/ports/src/noux/main.cc b/ports/src/noux/main.cc index 79c110cbb..648203b68 100644 --- a/ports/src/noux/main.cc +++ b/ports/src/noux/main.cc @@ -435,6 +435,13 @@ static char const *env_string_of_init_process() } +Noux::Pid_allocator *Noux::pid_allocator() +{ + static Noux::Pid_allocator inst; + return &inst; +} + + void *operator new (Genode::size_t size) { return Genode::env()->heap()->alloc(size); } diff --git a/ports/src/noux/ram_session_component.h b/ports/src/noux/ram_session_component.h new file mode 100644 index 000000000..0c546497a --- /dev/null +++ b/ports/src/noux/ram_session_component.h @@ -0,0 +1,196 @@ +/* + * \brief RAM service used by Noux processes + * \author Norman Feske + * \date 2012-02-22 + * + * The custom implementation of the RAM 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) 2012 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__RAM_SESSION_COMPONENT_H_ +#define _NOUX__RAM_SESSION_COMPONENT_H_ + +/* Genode includes */ +#include +#include +#include + +/* Noux includes */ +#include + +namespace Noux { + + using namespace Genode; + + struct Ram_dataspace_info : Dataspace_info, + List::Element + { + Ram_dataspace_info(Ram_dataspace_capability ds_cap, + Dataspace_destroyer &destroyer) + : Dataspace_info(ds_cap, destroyer) { } + + Dataspace_capability fork(Ram_session_capability ram, + Dataspace_destroyer &destroyer, + Dataspace_registry &, + Rpc_entrypoint &) + { + size_t const size = Dataspace_client(ds_cap()).size(); + + Ram_dataspace_capability dst_ds; + + try { + dst_ds = Ram_session_client(ram).alloc(size); + } catch (...) { + return Dataspace_capability(); + } + + void *src = 0; + try { + src = Genode::env()->rm_session()->attach(ds_cap()); + } catch (...) { } + + void *dst = 0; + try { + dst = Genode::env()->rm_session()->attach(dst_ds); + } catch (...) { } + + if (src && dst) + memcpy(dst, src, size); + + if (src) Genode::env()->rm_session()->detach(src); + if (dst) Genode::env()->rm_session()->detach(dst); + + if (!src || !dst) { + Ram_session_client(ram).free(dst_ds); + return Dataspace_capability(); + } + + return dst_ds; + } + + void poke(addr_t dst_offset, void const *src, size_t len) + { + if ((dst_offset >= size()) || (dst_offset + len > size())) { + PERR("illegal attemt to write beyond dataspace boundary"); + return; + } + + char *dst = 0; + try { + dst = Genode::env()->rm_session()->attach(ds_cap()); + } catch (...) { } + + if (src && dst) + memcpy(dst + dst_offset, src, len); + + if (dst) Genode::env()->rm_session()->detach(dst); + } + }; + + + class Ram_session_component : public Rpc_object, + public Dataspace_destroyer + { + private: + + List _list; + + /* + * Track the RAM resources accumulated via RAM session allocations. + * + * XXX not used yet + */ + size_t _used_quota; + + Dataspace_registry &_registry; + + public: + + /** + * Constructor + */ + Ram_session_component(Dataspace_registry ®istry) + : _used_quota(0), _registry(registry) { } + + /** + * Destructor + */ + ~Ram_session_component() + { + Ram_dataspace_info *info = 0; + while ((info = _list.first())) + free(static_cap_cast(info->ds_cap())); + } + + + /*********************************** + ** Dataspace_destroyer interface ** + ***********************************/ + + /* + * XXX not used yet + */ + void destroy(Dataspace_capability ds) + { + free(static_cap_cast(ds)); + } + + + /*************************** + ** Ram_session interface ** + ***************************/ + + Ram_dataspace_capability alloc(size_t size) + { + Ram_dataspace_capability ds_cap = env()->ram_session()->alloc(size); + + Ram_dataspace_info *ds_info = new (env()->heap()) + Ram_dataspace_info(ds_cap, *this); + + _used_quota += ds_info->size(); + + _registry.insert(ds_info); + _list.insert(ds_info); + + return ds_cap; + } + + void free(Ram_dataspace_capability ds_cap) + { + Ram_dataspace_info *ds_info = + dynamic_cast(_registry.lookup_info(ds_cap)); + + if (!ds_info) { + PERR("RAM free: dataspace lookup failed"); + return; + } + + _registry.remove(ds_info); + _list.remove(ds_info); + _used_quota -= ds_info->size(); + + env()->ram_session()->free(ds_cap); + Genode::destroy(env()->heap(), ds_info); + } + + int ref_account(Ram_session_capability) { return 0; } + int transfer_quota(Ram_session_capability, size_t) { return 0; } + size_t quota() { return env()->ram_session()->quota(); } + size_t used() { return _used_quota; } + }; +} + +#endif /* _NOUX__RAM_SESSION_COMPONENT_H_ */ diff --git a/ports/src/noux/rm_session_component.h b/ports/src/noux/rm_session_component.h new file mode 100644 index 000000000..fe19cfefa --- /dev/null +++ b/ports/src/noux/rm_session_component.h @@ -0,0 +1,265 @@ +/* + * \brief RM session implementation used by Noux processes + * \author Norman Feske + * \date 2012-02-22 + * + * The custom RM implementation is used for recording all RM regions attached + * to the region-manager session. Using the recorded information, the address- + * space layout can then be replayed onto a new process created via fork. + */ + +/* + * Copyright (C) 2012 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__RM_SESSION_COMPONENT_H_ +#define _NOUX__RM_SESSION_COMPONENT_H_ + +/* Genode includes */ +#include +#include + +namespace Noux { + + using namespace Genode; + + class Rm_session_component : public Rpc_object + { + private: + + /** + * Record of an attached dataspace + */ + struct Region : List::Element + { + Dataspace_capability ds; + size_t size; + off_t offset; + addr_t local_addr; + + Region(Dataspace_capability ds, size_t size, + off_t offset, addr_t local_addr) + : + ds(ds), size(size), offset(offset), local_addr(local_addr) + { } + + /** + * Return true if region contains specified address + */ + bool contains(addr_t addr) const + { + return (addr >= local_addr) + && (addr < local_addr + size); + } + }; + + List _regions; + + Region *_lookup_region_by_addr(addr_t local_addr) + { + Region *curr = _regions.first(); + for (; curr; curr = curr->next()) { + if (curr->contains(local_addr)) + return curr; + } + return 0; + } + + /** + * Wrapped RM session at core + */ + Rm_connection _rm; + + Dataspace_registry &_ds_registry; + + public: + + Rm_session_component(Dataspace_registry &ds_registry, + addr_t start = ~0UL, size_t size = 0) + : + _rm(start, size), _ds_registry(ds_registry) + { } + + ~Rm_session_component() + { + Region *curr = 0; + for (; curr; curr = curr->next()) + detach(curr->local_addr); + } + + /** + * Replay attachments onto specified RM session + * + * \param dst_ram backing store used for allocating the + * the copies of RAM dataspaces + * \param ds_registry dataspace registry used for keeping track + * of newly created dataspaces + * \param ep entrypoint used to serve the RPC interface + * of forked managed dataspaces + */ + void replay(Ram_session_capability dst_ram, + Rm_session_capability dst_rm, + Dataspace_registry &ds_registry, + Rpc_entrypoint &ep) + { + /* + * XXX remove this + */ + struct Dummy_destroyer : Dataspace_destroyer + { + void destroy(Dataspace_capability) { } + }; + + static Dummy_destroyer dummy_destroyer; + + for (Region *curr = _regions.first(); curr; curr = curr->next()) { + + Dataspace_capability ds; + + Dataspace_info *info = _ds_registry.lookup_info(curr->ds); + + if (info) { + + /* + * XXX Using 'this' as destroyer may be false, the + * destroyer should better correspond to dst_ram. + */ + ds = info->fork(dst_ram, dummy_destroyer, ds_registry, ep); + + /* + * XXX We could detect dataspaces that are attached + * more than once. For now, we create a new fork + * for each attachment. + */ + + } else { + + /* + * If the dataspace is not a RAM dataspace, assume that + * it's a ROM dataspace. + * + * XXX Handle ROM dataspaces explicitly. For once, we + * need to make sure that they remain available + * until the child process exits even if the parent + * process exits earlier. Furthermore, we would + * like to detect unexpected dataspaces. + */ + ds = curr->ds; + PWRN("replay: unknown dataspace type, assume ROM"); + } + + if (!ds.valid()) { + PERR("replay: Error while forking dataspace"); + continue; + } + + Rm_session_client(dst_rm).attach(ds, curr->size, + curr->offset, + true, + curr->local_addr); + } + } + + void poke(addr_t dst_addr, void const *src, size_t len) + { + Region *region = _lookup_region_by_addr(dst_addr); + if (!region) { + PERR("poke: no region at 0x%lx", dst_addr); + return; + } + + /* + * Test if start and end address occupied by the object + * type refers to the same region. + */ + if (region != _lookup_region_by_addr(dst_addr + len - 1)) { + PERR("attempt to write beyond region boundary"); + return; + } + + Dataspace_info *info = _ds_registry.lookup_info(region->ds); + if (!info) { + PERR("attempt to write to unknown dataspace type"); + for (;;); + return; + } + + if (region->offset) { + PERR("poke: writing to region with offset is not supported"); + return; + } + + info->poke(dst_addr - region->local_addr, src, len); + } + + + /************************** + ** RM session interface ** + **************************/ + + Local_addr attach(Dataspace_capability ds, + size_t size = 0, off_t offset = 0, + bool use_local_addr = false, + Local_addr local_addr = (addr_t)0) + { + if (size == 0) + size = Dataspace_client(ds).size(); + + /* + * 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); + + /* + * Record attachement for later replay (needed during + * fork) + */ + _regions.insert(new (Genode::env()->heap()) + Region(ds, size, offset, local_addr)); + return local_addr; + } + + void detach(Local_addr local_addr) + { + _rm.detach(local_addr); + + Region *region = _lookup_region_by_addr(local_addr); + if (!region) { + PWRN("Attempt to detach unknown region at 0x%p", + (void *)local_addr); + return; + } + + _regions.remove(region); + destroy(Genode::env()->heap(), region); + } + + Pager_capability add_client(Thread_capability thread) + { + return _rm.add_client(thread); + } + + void fault_handler(Signal_context_capability handler) + { + return _rm.fault_handler(handler); + } + + State state() + { + return _rm.state(); + } + + Dataspace_capability dataspace() + { + return _rm.dataspace(); + } + }; +} + +#endif /* _NOUX__RM_SESSION_COMPONENT_H_ */