From cbd4830977370d4403812fc068405c34fb0c8ab0 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Wed, 22 Feb 2012 12:45:13 +0100 Subject: [PATCH] Propagate new parent cap to forked process This patch removes the hardcoded parent cap for the new child by introducing a facility called 'poke' similar to copy-to-user. This change makes the fork mechanism kernel-agnostic. The 'noux_fork' test works on L4/Fiasco, OKL4, NOVA, Fiasco.OC, and L4ka::Pistachio. Linux is not supported yet. --- ports/run/noux_bash.run | 2 + ports/src/lib/libc_noux/plugin.cc | 96 +++++++++++------ ports/src/noux/child.h | 165 +++++++++++++++++++++++++----- ports/src/noux/main.cc | 10 +- 4 files changed, 214 insertions(+), 59 deletions(-) diff --git a/ports/run/noux_bash.run b/ports/run/noux_bash.run index 37af9d3fcd..706e667911 100644 --- a/ports/run/noux_bash.run +++ b/ports/run/noux_bash.run @@ -15,11 +15,13 @@ set build_components { server/terminal } lappend build_components noux-pkg/bash +lappend build_components noux-pkg/coreutils lappend build_components test/libports/ncurses build $build_components exec tar cfv bin/bash.tar -h -C bin/bash . +exec tar rfv bin/bash.tar -h -C bin/coreutils . create_boot_directory diff --git a/ports/src/lib/libc_noux/plugin.cc b/ports/src/lib/libc_noux/plugin.cc index b1b1fea966..bd02b7c911 100644 --- a/ports/src/lib/libc_noux/plugin.cc +++ b/ports/src/lib/libc_noux/plugin.cc @@ -33,42 +33,39 @@ #include #include #include +#include #include #include +void *operator new (size_t, void *ptr) { return ptr; } + class Noux_connection { private: - Noux::Connection *_connection; + Noux::Connection _connection; Noux::Sysio *_sysio; - void _connect() + Noux::Sysio *_obtain_sysio() { - _connection = new (Genode::env()->heap()) Noux::Connection; - _sysio = Genode::env()->rm_session()->attach(_connection->sysio_dataspace()); - } - - void _disconnect() - { - Genode::env()->rm_session()->detach(_sysio); - Genode::destroy(Genode::env()->heap(), _connection); + return Genode::env()->rm_session()->attach(_connection.sysio_dataspace()); } public: - Noux_connection() { _connect(); } + Noux_connection() : _sysio(_obtain_sysio()) { } - void reconnect() { - _disconnect(); _connect(); - PDBG("reconnected to Noux cap=%lx", _connection->cap().local_name()); - PDBG("getpid returns %d", getpid()); + void reconnect() + { + new (&_connection) Noux_connection; + Genode::env()->rm_session()->detach(_sysio); + _sysio = _obtain_sysio(); } - Noux::Session *session() { return _connection; } - Noux::Sysio *sysio() { return _sysio; } + Noux::Session *session() { return &_connection; } + Noux::Sysio *sysio() { return _sysio; } }; @@ -320,31 +317,26 @@ extern "C" int select(int nfds, fd_set *readfds, fd_set *writefds, return out_fds.total_fds(); } - #include - #include static jmp_buf fork_jmp_buf; static Genode::Capability new_parent_cap; +extern "C" void stdout_reconnect(); /* provided by 'log_console.cc' */ + /* * The new process created via fork will start its execution here. */ extern "C" void fork_trampoline() { - /* - * XXX remove hard-coded parent cap - */ - Okl4::L4_ThreadId_t tid; - tid.raw = 0x210001UL; - Genode::Native_capability cap(tid, 0x107UL); - new_parent_cap = Genode::reinterpret_cap_cast(cap); - static_cast(Genode::env())->reload_parent_cap(new_parent_cap); + stdout_reconnect(); + noux_connection()->reconnect(); + longjmp(fork_jmp_buf, 1); } @@ -357,8 +349,9 @@ extern "C" pid_t fork(void) if (setjmp(fork_jmp_buf)) { - /* got here via longjmp from 'fork_trampoline' */ - noux_connection()->reconnect(); + /* + * We got here via longjmp from 'fork_trampoline'. + */ return 0; } else { @@ -384,6 +377,51 @@ extern "C" pid_t getpid(void) } +extern "C" pid_t _wait4(pid_t pid, int *status, int options, + struct rusage *rusage) +{ + /* + * XXX dummy to accomodate the 'reap_zombie_children' function in bash + */ + if (options & WNOHANG) { + PWRN("_wait4 dummy called (with WNOHANG) - not implemented"); + return 0; + } + + PDBG("_wait4 (pid=%d, options=0x%x) called, waiting forever...", + pid, options); + + for (;;); + return 0; +} + + +/******************** + ** Time functions ** + ********************/ + +/* + * The default implementations as provided by the libc rely on a dedicated + * thread. But on Noux, no thread other than the main thread is allowed. For + * this reason, we need to override the default implementations here. + */ + +extern "C" int clock_gettime(clockid_t clk_id, struct timespec *tp) +{ + PDBG("clock_gettime called - not implemented"); + errno = EINVAL; + return -1; +} + + +extern "C" int gettimeofday(struct timeval *tv, struct timezone *tz) +{ + PDBG("gettimeofdaye called - not implemented"); + errno = EINVAL; + return -1; +} + + /********************* ** File operations ** *********************/ diff --git a/ports/src/noux/child.h b/ports/src/noux/child.h index 8866fada35..4eb7450c5e 100644 --- a/ports/src/noux/child.h +++ b/ports/src/noux/child.h @@ -163,6 +163,10 @@ namespace Noux { virtual void destroy(Dataspace_capability) = 0; }; + + class Dataspace_registry; + + class Dataspace_info : public Object_pool::Entry { private: @@ -185,7 +189,30 @@ namespace Noux { size_t size() const { return _size; } Dataspace_capability ds_cap() const { return _ds_cap; } - virtual Dataspace_capability fork(Ram_session_capability ram) = 0; + /** + * 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; } }; @@ -223,7 +250,10 @@ namespace Noux { Dataspace_destroyer &destroyer) : Dataspace_info(ds_cap, destroyer) { } - Dataspace_capability fork(Ram_session_capability ram) + Dataspace_capability fork(Ram_session_capability ram, + Dataspace_destroyer &destroyer, + Dataspace_registry &, + Rpc_entrypoint &) { size_t const size = Dataspace_client(ds_cap()).size(); @@ -232,7 +262,7 @@ namespace Noux { try { dst_ds = Ram_session_client(ram).alloc(size); } catch (...) { - return Ram_dataspace_capability(); + return Dataspace_capability(); } void *src = 0; @@ -253,11 +283,29 @@ namespace Noux { if (!src || !dst) { Ram_session_client(ram).free(dst_ds); - return Ram_dataspace_capability(); + 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); + } }; @@ -295,7 +343,7 @@ namespace Noux { ** Dataspace_destroyer interface ** ***********************************/ - /* XXX not yet used */ + /* XXX not used yet */ void destroy(Dataspace_capability ds) { free(static_cap_cast(ds)); @@ -409,8 +457,19 @@ namespace Noux { * the copies of RAM dataspaces */ void replay(Ram_session_capability dst_ram, - Rm_session_capability dst_rm) + 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()) { @@ -419,7 +478,15 @@ namespace Noux { Dataspace_info *info = _ds_registry.lookup_info(curr->ds); if (info) { - ds = info->fork(dst_ram); + /* + * 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"); @@ -443,7 +510,7 @@ namespace Noux { * detect unexpected dataspaces. */ ds = curr->ds; - PWRN("replay: unknown dataspace type"); + PWRN("replay: unknown dataspace type, assume ROM"); } Rm_session_client(dst_rm).attach(ds, curr->size, @@ -453,6 +520,38 @@ namespace Noux { } } + 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 ** @@ -535,20 +634,30 @@ namespace Noux { _sub_rm(sub_rm) { } - Dataspace_capability fork(Ram_session_capability ram) + Dataspace_capability fork(Ram_session_capability ram, + Dataspace_destroyer &destoyer, + Dataspace_registry &ds_registry, + Rpc_entrypoint &ep) { - Rm_connection *new_sub_rm = new Rm_connection(0, size()); + Local_rm_session *new_sub_rm = + new Local_rm_session(ds_registry, 0, size()); - /* - * XXX We are leaking the 'Rm_connection' because of - * keeping only the dataspace. - */ + Rm_session_capability rm_cap = ep.manage(new_sub_rm); - _sub_rm.replay(ram, new_sub_rm->cap()); + _sub_rm.replay(ram, rm_cap, ds_registry, ep); - Dataspace_capability ds_cap = new_sub_rm->dataspace(); + ds_registry.insert(new Sub_rm_dataspace_info(*new_sub_rm)); - return ds_cap; + 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); } }; @@ -573,6 +682,7 @@ namespace Noux { */ if (main_thread.valid()) { PWRN("Invalid attempt to create a thread besides main"); + while (1); return Thread_capability(); } main_thread = cpu.create_thread(name); @@ -804,7 +914,7 @@ namespace Noux { _args(ARGS_DS_SIZE, args), _env(env), _vfs(vfs), - _binary_ds(forked ? Dataspace_capability() + _binary_ds(forked ? Dataspace_capability() : vfs->dataspace_from_file(name)), _child(_binary_ds, _resources.ram.cap(), _resources.cpu.cap(), _resources.rm.cap(), &_entrypoint, this), @@ -821,11 +931,6 @@ namespace Noux { { _args.dump(); strncpy(_name, name, sizeof(_name)); - - Native_capability cap = _child.parent_cap(); - - PINF("PID %d has parent cap %lx,%lx", - _pid, cap.tid().raw, cap.local_name()); } ~Child() @@ -837,17 +942,21 @@ namespace Noux { void start() { _entrypoint.activate(); } - void start_forked_main_thread(addr_t ip, addr_t sp, addr_t /* parent_cap_addr */) + void start_forked_main_thread(addr_t ip, addr_t sp, addr_t parent_cap_addr) { - /* - * XXX poke parent_cap_addr into child's address space - */ + /* poke parent_cap_addr into child's address space */ + Parent_capability const cap = _child.parent_cap(); + _resources.rm.poke(parent_cap_addr, &cap, sizeof(cap)); + + /* start execution of new main thread at supplied trampoline */ _resources.cpu.start_main_thread(ip, sp); } 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; } + /**************************** ** Child_policy interface ** diff --git a/ports/src/noux/main.cc b/ports/src/noux/main.cc index f9ec9b8bb4..79c110cbba 100644 --- a/ports/src/noux/main.cc +++ b/ports/src/noux/main.cc @@ -284,7 +284,12 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) int new_pid = pid_allocator()->alloc(); char const *env = "PWD=\"/\""; - Child *child = new Child("borked" /*name()*/, + /* + * XXX To ease debugging, it would be useful to generate a + * unique name that includes the PID instead of just + * reusing the name of the parent. + */ + Child *child = new Child(name(), new_pid, _sig_rec, _vfs, @@ -298,7 +303,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) _assign_io_channels_to(child); /* copy our address space into the new child */ - _resources.rm.replay(child->ram(), child->rm()); + _resources.rm.replay(child->ram(), child->rm(), + child->ds_registry(), _resources.ep); /* start executing the main thread of the new process */ child->start_forked_main_thread(ip, sp, parent_cap_addr);