genode/repos/dde_rump/src/lib/rump/hypercall.cc

373 lines
7.1 KiB
C++

/**
* \brief Rump hypercall-interface implementation
* \author Sebastian Sumpf
* \author Josef Soentgen
* \date 2013-12-06
*/
/*
* Copyright (C) 2013-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.
*/
#include "sched.h"
#include <base/log.h>
#include <base/sleep.h>
#include <rump/env.h>
#include <util/allocator_fap.h>
#include <util/random.h>
#include <util/string.h>
enum {
SUPPORTED_RUMP_VERSION = 17,
MAX_VIRTUAL_MEMORY = (sizeof(void *) == 4 ? 256UL : 4096UL) * 1024 * 1024
};
static bool verbose = false;
/* upcalls to rump kernel */
struct rumpuser_hyperup _rump_upcalls;
/*************
** Threads **
*************/
static Hard_context *main_context()
{
static Hard_context inst(0);
return &inst;
}
static Hard_context *myself()
{
Hard_context *h = Hard_context_registry::r().find(Genode::Thread::myself());
if (!h)
Genode::error("Hard context is nullptr (", Genode::Thread::myself(), ")");
return h;
}
Timer::Connection &Hard_context::timer()
{
static Timer::Connection _timer { Rump::env().env() };
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 long count = 0;
if (mustjoin)
*cookie = (void *)++count;
new (Rump::env().heap()) Hard_context_thread(name, f, arg, mustjoin ? count : 0);
return 0;
}
void rumpuser_thread_exit()
{
Genode::sleep_forever();
}
int errno;
void rumpuser_seterrno(int e) { errno = e; }
/********************
** Initialization **
********************/
int rumpuser_init(int version, const struct rumpuser_hyperup *hyp)
{
if (version != SUPPORTED_RUMP_VERSION) {
Genode::error("unsupported rump-kernel version (", version, ") - "
"supported is ", (int)SUPPORTED_RUMP_VERSION);
return -1;
}
_rump_upcalls = *hyp;
/* register context for main EP */
main_context()->thread(Genode::Thread::myself());
Hard_context_registry::r().insert(main_context());
return 0;
}
/*************************
** Parameter retrieval **
*************************/
static size_t _rump_memlimit = 0;
void rump_set_memlimit(Genode::size_t limit)
{
_rump_memlimit = limit;
}
int rumpuser_getparam(const char *name, void *buf, size_t buflen)
{
enum {
MIN_RESERVE_MEM = 1U << 20,
MIN_RUMP_MEM = 6U << 20,
};
/* support one cpu */
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")) {
if (!_rump_memlimit) {
Genode::error("no RAM limit set");
throw -1;
}
/*
* Set RAM limit and reserve a 10th or at least 1MiB for
* Genode meta-data.
*/
Genode::size_t rump_ram = _rump_memlimit;
size_t const reserve = Genode::max((size_t)MIN_RESERVE_MEM, rump_ram / 10);
if (reserve < MIN_RESERVE_MEM) {
Genode::error("could not reserve enough RAM for meta-data, need at least ",
(size_t)MIN_RESERVE_MEM >> 20, " MiB");
throw -1;
}
rump_ram -= reserve;
/* check RAM limit is enough... */
if (rump_ram < MIN_RUMP_MEM) {
Genode::error("RAM limit too small, need at least ",
(size_t)MIN_RUMP_MEM >> 20, " MiB");
throw -1;
}
/* ... and is in valid range (overflow) */
if (rump_ram >= _rump_memlimit) {
Genode::error("rump RAM limit invalid");
throw -1;
}
rump_ram = Genode::min((unsigned long)MAX_VIRTUAL_MEMORY, rump_ram);
/* convert to string */
Genode::snprintf((char *)buf, buflen, "%zu", rump_ram);
Genode::log("asserting rump kernel ", rump_ram / 1024, " KB of RAM");
return 0;
}
return -1;
}
/*************
** Console **
*************/
void rumpuser_putchar(int ch)
{
enum { BUF_SIZE = 256 };
static unsigned char buf[BUF_SIZE];
static int count = 0;
if (count < BUF_SIZE - 1 && ch != '\n')
buf[count++] = (unsigned char)ch;
if (ch == '\n' || count == BUF_SIZE - 1) {
buf[count] = 0;
int nlocks;
if (myself() != main_context())
rumpkern_unsched(&nlocks, 0);
Genode::log("rump: ", Genode::Cstring((char const *)buf));
if (myself() != main_context())
rumpkern_sched(nlocks, 0);
count = 0;
}
}
/************
** Memory **
************/
struct Allocator_policy
{
static int block()
{
int nlocks;
if (myself() != main_context())
rumpkern_unsched(&nlocks, 0);
return nlocks;
}
static void unblock(int nlocks)
{
if (myself() != main_context())
rumpkern_sched(nlocks, 0);
}
};
typedef Allocator::Fap<MAX_VIRTUAL_MEMORY, Allocator_policy> Rump_alloc;
static Genode::Lock & alloc_lock()
{
static Genode::Lock inst;
return inst;
}
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 = alignment ? Genode::log2(alignment) : 0;
*memp = allocator()->alloc(len, align);
if (verbose)
Genode::log("ALLOC: p: ", *memp, ", s: ", len, ", a: ", align, " ", alignment);
return *memp ? 0 : -1;
}
void rumpuser_free(void *mem, size_t len)
{
Genode::Lock::Guard guard(alloc_lock());
allocator()->free(mem, len);
if (verbose)
Genode::warning("FREE: p: ", mem, ", s: ", len);
}
/************
** Clocks **
************/
int rumpuser_clock_gettime(int enum_rumpclock, int64_t *sec, long *nsec)
{
Hard_context *h = myself();
Genode::uint64_t 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;
Genode::uint64_t msec = 0;
Timer::Connection &timer = myself()->timer();
rumpkern_unsched(&nlocks, 0);
switch (enum_rumpclock) {
case RUMPUSER_CLOCK_RELWALL:
msec = (Genode::uint64_t)sec * 1000 + nsec / (1000*1000UL);
break;
case RUMPUSER_CLOCK_ABSMONO:
msec = timer.elapsed_ms();
msec = (((Genode::uint64_t)sec * 1000) + ((Genode::uint64_t)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)
{
/*
* Cast retp to Genode::size_t to prevent compiler error because
* the type of rump's size_t is int on 32 bit and long 64 bit archs.
*/
return rumpuser_getrandom_backend(buf, buflen, flags,
(Genode::size_t *)retp);
}
/**********
** Exit **
**********/
void genode_exit(int) __attribute__((noreturn));
void rumpuser_exit(int status)
{
if (status == RUMPUSER_PANIC)
Genode::error("Rump panic");
genode_exit(status);
}