/** * \brief Rump hypercall-interface implementation * \author Sebastian Sumpf * \author Josef Soentgen * \date 2013-12-06 */ /* * Copyright (C) 2013-2014 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. */ #include "sched.h" #include #include #include #include #include #include extern "C" void wait_for_continue(); enum { SUPPORTED_RUMP_VERSION = 17 }; static bool verbose = false; /* upcalls to rump kernel */ struct rumpuser_hyperup _rump_upcalls; /******************** ** Initialization ** ********************/ int rumpuser_init(int version, const struct rumpuser_hyperup *hyp) { PDBG("RUMP ver: %d", version); if (version != SUPPORTED_RUMP_VERSION) { PERR("Unsupported rump-kernel version (%d) - supported is %d)", version, SUPPORTED_RUMP_VERSION); return -1; } _rump_upcalls = *hyp; /* * Start 'Timeout_thread' so it does not get constructed concurrently (which * causes one thread to spin in cxa_guard_aqcuire), making emulation *really* * slow */ Genode::Timeout_thread::alarm_timer(); return 0; } /************* ** Threads ** *************/ static Hard_context _main_thread("main thread", 0, 0, 0, false); static Hard_context *myself() { Hard_context *h = dynamic_cast(Genode::Thread_base::myself()); return h ? h : &_main_thread; } Timer::Connection *Hard_context::timer() { static Timer::Connection _timer; return &_timer; } void rumpuser_curlwpop(int enum_rumplwpop, struct lwp *l) { Hard_context *h = myself(); switch (enum_rumplwpop) { case RUMPUSER_LWP_CREATE: case RUMPUSER_LWP_DESTROY: break; case RUMPUSER_LWP_SET: h->set_lwp(l); break; case RUMPUSER_LWP_CLEAR: h->set_lwp(0); break; } } struct lwp * rumpuser_curlwp(void) { return myself()->get_lwp(); } int rumpuser_thread_create(func f, void *arg, const char *name, int mustjoin, int priority, int cpui_dx, void **cookie) { static int count = 0; if (mustjoin) *cookie = (void *)++count; new (Genode::env()->heap()) Hard_context(name, f, arg, mustjoin ? count : 0); return 0; } int errno; void rumpuser_seterrno(int e) { errno = e; } /************************* ** Parameter retrieval ** *************************/ int rumpuser_getparam(const char *name, void *buf, size_t buflen) { /* support one cpu */ PDBG("%s", name); if (!Genode::strcmp(name, "_RUMPUSER_NCPU")) { Genode::strncpy((char *)buf, "1", 2); return 0; } /* return out cool host name */ if (!Genode::strcmp(name, "_RUMPUSER_HOSTNAME")) { Genode::strncpy((char *)buf, "rump4genode", 12); return 0; } if (!Genode::strcmp(name, "RUMP_MEMLIMIT")) { /* leave 2 MB for the Genode */ size_t rump_ram = Genode::env()->ram_session()->avail() - (2 * 1024 * 1024); /* convert to string */ Genode::snprintf((char *)buf, buflen, "%zu", rump_ram); PINF("Asserting rump kernel %zu KB of RAM", rump_ram / 1024); return 0; } return -1; } /************* ** Console ** *************/ void rumpuser_putchar(int ch) { static unsigned char buf[256]; static int count = 0; buf[count++] = (unsigned char)ch; if (ch == '\n') { buf[count] = 0; int nlocks; if (myself() != &_main_thread) rumpkern_unsched(&nlocks, 0); PLOG("rump: %s", buf); if (myself() != &_main_thread) rumpkern_sched(nlocks, 0); count = 0; } } /************ ** Memory ** ************/ struct Allocator_policy { static int block() { int nlocks; if (myself() != &_main_thread) rumpkern_unsched(&nlocks, 0); return nlocks; } static void unblock(int nlocks) { if (myself() != &_main_thread) rumpkern_sched(nlocks, 0); } }; typedef Allocator::Fap<128 * 1024 * 1024, Allocator_policy> Rump_alloc; static Genode::Lock _alloc_lock; static Rump_alloc* allocator() { static Rump_alloc _fap(true); return &_fap; } int rumpuser_malloc(size_t len, int alignment, void **memp) { Genode::Lock::Guard guard(_alloc_lock); int align = Genode::log2(alignment); *memp = allocator()->alloc(len, align); if (verbose) PWRN("ALLOC: p: %p, s: %zx, a: %d", *memp, len, align); return *memp ? 0 : -1; } void rumpuser_free(void *mem, size_t len) { Genode::Lock::Guard guard(_alloc_lock); allocator()->free(mem, len); if (verbose) PWRN("FREE: p: %p, s: %zx", mem, len); } /************ ** Clocks ** ************/ int rumpuser_clock_gettime(int enum_rumpclock, int64_t *sec, long *nsec) { Hard_context *h = myself(); unsigned long t = h->timer()->elapsed_ms(); *sec = (int64_t)t / 1000; *nsec = (t % 1000) * 1000; return 0; } int rumpuser_clock_sleep(int enum_rumpclock, int64_t sec, long nsec) { int nlocks; unsigned int msec = 0; Timer::Connection *timer = myself()->timer(); rumpkern_unsched(&nlocks, 0); switch (enum_rumpclock) { case RUMPUSER_CLOCK_RELWALL: msec = sec * 1000 + nsec / (1000*1000UL); break; case RUMPUSER_CLOCK_ABSMONO: msec = timer->elapsed_ms(); msec = ((sec * 1000) + (nsec / (1000 * 1000))) - msec; break; } timer->msleep(msec); rumpkern_sched(nlocks, 0); return 0; } /***************** ** Random pool ** *****************/ int rumpuser_getrandom(void *buf, size_t buflen, int flags, size_t *retp) { Timer::Connection *timer = myself()->timer(); uint8_t *rndbuf; for (*retp = 0, rndbuf = (uint8_t *)buf; *retp < buflen; (*retp)++) { *rndbuf++ = timer->elapsed_ms() & 0xff; timer->msleep(timer->elapsed_ms() & 0xf); } return 0; }