lx_kit: add modular lx_emul backend

The modular lx_kit seperates the required back end functionality of the
Linux emulation environment from the front end. Thereby each driver can
reuse specific parts or supply more suitable implementations by itself.
It is used to reduce the amount of redundant code in each driver.

The lx_kit is split into several layers whose structure is as follows:

The first layer in _repos/dde_linux/src/include/lx_emul_ contains those
header files that provide the structural definitions and function
declarations of the Linux API, e.g. _errno.h_ provides all error code
values. The second layer in _repos/dde_linux/src/include/lx_emul/impl_
contains the implementation of selected functions, e.g. _slab.h_
provides the implementation of 'kmalloc()'. The lx_kit back end API is
the third layer and provides the _Lx::Malloc_ interface
(_repos/dde_linux/src/include/lx_kit/malloc.h_) which is used to
implement 'kmalloc()'. There are several generic implementations of the
lx_kit interfaces that can be used by a driver.

A driver typically includes a 'lx_emul/impl/xyz.h' header once
directly in its lx_emul compilation unit. The lx_kit interface files
are only included in those compilation units that use or implement the
interface. If a driver wants to use a generic implementation it must
add the source file to its source file list. The generic
implementations are located in _repos/dde_linux/src/lx_kit/_.

The modular lx_kit still depends on the private _lx_emul.h_ header file
that is tailored to each driver. Since the lx_kit already contains much
of the declarations and definitions that were originally placed in
these private header files, those files can now ommit a large amount
of code.

Fixes #1974.
This commit is contained in:
Josef Söntgen
2016-03-17 15:19:03 +01:00
committed by Christian Helmuth
parent ee05fb9259
commit 0106045bad
77 changed files with 2608 additions and 1186 deletions

View File

@ -0,0 +1,224 @@
/*
* \brief Signal context for IRQ's
* \author Josef Soentgen
* \author Christian Helmuth
* \author Stefan Kalkowski
* \date 2014-10-14
*/
/*
* Copyright (C) 2014-2016 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.
*/
/* Genode includes */
#include <base/snprintf.h>
#include <base/tslab.h>
#include <irq_session/client.h>
/* Linux emulation environment */
#include <lx_emul.h>
/* Linux kit includes */
#include <lx_kit/irq.h>
#include <lx_kit/scheduler.h>
namespace Lx_kit { class Irq; }
class Lx_kit::Irq : public Lx::Irq
{
private:
/**
* Helper utilty for composing IRQ related names
*/
struct Name_composer
{
char name[16];
Name_composer(Platform::Device &device)
{
Genode::snprintf(name, sizeof(name),
"irq_%02x:%02x",
device.vendor_id(),
device.device_id());
}
};
/**
* This contains the Linux-driver handlers
*/
class Handler : public Lx_kit::List<Handler>::Element
{
private:
void *_dev; /* Linux device */
irq_handler_t _handler; /* Linux handler */
irq_handler_t _thread_fn; /* Linux thread function */
public:
Handler(void *dev, irq_handler_t handler,
irq_handler_t thread_fn)
: _dev(dev), _handler(handler), _thread_fn(thread_fn) { }
bool handle()
{
switch (_handler(0, _dev)) {
case IRQ_WAKE_THREAD:
_thread_fn(0, _dev);
case IRQ_HANDLED:
return true;
case IRQ_NONE:
break;
}
return false;
}
};
public:
/**
* Context encapsulates the handling of an IRQ
*/
class Context : public Lx_kit::List<Context>::Element
{
private:
Name_composer _name;
Platform::Device &_dev;
Genode::Irq_session_client _irq_sess;
Lx_kit::List<Handler> _handler;
Lx::Task _task;
Genode::Signal_rpc_member<Context> _dispatcher;
/**
* Signal handler
*/
void _handle(unsigned)
{
_task.unblock();
/* kick off scheduling */
Lx::scheduler().schedule();
}
static void _run_irq(void *args)
{
Context *ctx = static_cast<Context*>(args);
while (1) {
Lx::scheduler().current()->block_and_schedule();
ctx->handle_irq();
}
}
public:
/**
* Constructor
*/
Context(Server::Entrypoint &ep,
Platform::Device &dev)
:
_name(dev),
_dev(dev),
_irq_sess(dev.irq(0)),
_task(_run_irq, this, _name.name, Lx::Task::PRIORITY_3, Lx::scheduler()),
_dispatcher(ep, *this, &Context::_handle)
{
_irq_sess.sigh(_dispatcher);
/* initial ack to receive further IRQ signals */
_irq_sess.ack_irq();
}
/**
* Handle IRQ
*/
void handle_irq()
{
/* report IRQ to all clients */
for (Handler *h = _handler.first(); h; h = h->next()) {
h->handle();
}
_irq_sess.ack_irq();
}
/**
* Add linux handler to context
*/
void add_handler(Handler *h) { _handler.append(h); }
bool device(Platform::Device &dev) {
return (&dev == &_dev); }
};
private:
using Context_slab = Genode::Tslab<Context, 3 * sizeof(Context)>;
using Handler_slab = Genode::Tslab<Handler, 3 * sizeof(Handler)>;
Server::Entrypoint &_ep;
Lx_kit::List<Context> _list;
Context_slab _context_alloc;
Handler_slab _handler_alloc;
/**
* Find context for given device
*/
Context *_find_context(Platform::Device &dev)
{
for (Context *i = _list.first(); i; i = i->next())
if (i->device(dev)) return i;
return nullptr;
}
Irq(Server::Entrypoint &ep, Genode::Allocator &alloc)
: _ep(ep),
_context_alloc(&alloc),
_handler_alloc(&alloc) { }
public:
static Irq &irq(Server::Entrypoint &ep, Genode::Allocator &alloc)
{
static Irq inst(ep, alloc);
return inst;
}
/***********************
** Lx::Irq interface **
***********************/
void request_irq(Platform::Device &dev, irq_handler_t handler,
void *dev_id, irq_handler_t thread_fn = 0) override
{
Context *ctx = _find_context(dev);
/* if this IRQ is not registered */
if (!ctx) {
ctx = new (&_context_alloc) Context(_ep, dev);
_list.insert(ctx);
}
/* register Linux handler */
Handler *h = new (&_handler_alloc)
Handler(dev_id, handler, thread_fn);
ctx->add_handler(h);
}
};
/****************************
** Lx::Irq implementation **
****************************/
Lx::Irq &Lx::Irq::irq(Server::Entrypoint *ep, Genode::Allocator *alloc) {
return Lx_kit::Irq::irq(*ep, *alloc); }

View File

@ -0,0 +1,383 @@
/*
* \brief Linux kit memory allocator
* \author Sebastian Sumpf
* \author Josef Soentgen
* \author Norman Feske
* \date 2014-10-10
*/
/*
* Copyright (C) 2014-2016 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.
*/
/* Genode includes */
#include <base/allocator_avl.h>
#include <base/env.h>
#include <base/slab.h>
#include <dataspace/client.h>
#include <rm_session/connection.h>
#include <region_map/client.h>
/* Linux kit includes */
#include <lx_kit/types.h>
#include <lx_kit/backend_alloc.h>
#include <lx_kit/malloc.h>
namespace Lx_kit {
class Slab_backend_alloc;
class Slab_alloc;
class Malloc;
}
class Lx_kit::Slab_backend_alloc : public Lx::Slab_backend_alloc,
public Genode::Rm_connection,
public Genode::Region_map_client
{
private:
enum {
VM_SIZE = 64 * 1024 * 1024, /* size of VM region to reserve */
P_BLOCK_SIZE = 2 * 1024 * 1024, /* 2 MB physical contiguous */
V_BLOCK_SIZE = P_BLOCK_SIZE * 2, /* 2 MB virtual used, 2 MB virtual left free to avoid that Allocator_avl merges virtual contiguous regions which are physically non-contiguous */
ELEMENTS = VM_SIZE / V_BLOCK_SIZE, /* MAX number of dataspaces in VM */
};
addr_t _base; /* virt. base address */
Genode::Cache_attribute _cached; /* non-/cached RAM */
Genode::Ram_dataspace_capability _ds_cap[ELEMENTS]; /* dataspaces to put in VM */
addr_t _ds_phys[ELEMENTS]; /* physical bases of dataspaces */
int _index; /* current index in ds_cap */
Genode::Allocator_avl _range; /* manage allocations */
bool _alloc_block()
{
if (_index == ELEMENTS) {
PERR("Slab-backend exhausted!");
return false;
}
try {
_ds_cap[_index] = Lx::backend_alloc(P_BLOCK_SIZE, _cached);
/* attach at index * V_BLOCK_SIZE */
Region_map_client::attach_at(_ds_cap[_index], _index * V_BLOCK_SIZE, P_BLOCK_SIZE, 0);
/* lookup phys. address */
_ds_phys[_index] = Genode::Dataspace_client(_ds_cap[_index]).phys_addr();
} catch (...) { return false; }
/* return base + offset in VM area */
addr_t block_base = _base + (_index * V_BLOCK_SIZE);
++_index;
_range.add_range(block_base, P_BLOCK_SIZE);
return true;
}
public:
Slab_backend_alloc(Genode::Cache_attribute cached)
:
Region_map_client(Rm_connection::create(VM_SIZE)),
_cached(cached), _index(0), _range(Genode::env()->heap())
{
/* reserver attach us, anywere */
_base = Genode::env()->rm_session()->attach(dataspace());
}
static Slab_backend_alloc &mem()
{
static Lx_kit::Slab_backend_alloc inst(Genode::CACHED);
return inst;
}
static Slab_backend_alloc &dma()
{
static Lx_kit::Slab_backend_alloc inst(Genode::UNCACHED);
return inst;
}
/**************************************
** Lx::Slab_backend_alloc interface **
**************************************/
bool alloc(size_t size, void **out_addr) override
{
bool done = _range.alloc(size, out_addr);
if (done)
return done;
done = _alloc_block();
if (!done) {
PERR("Backend allocator exhausted\n");
return false;
}
return _range.alloc(size, out_addr);
}
void free(void *addr) {
_range.free(addr); }
void free(void *addr, size_t size) override { _range.free(addr, size); }
size_t overhead(size_t size) const override { return 0; }
bool need_size_for_free() const override { return false; }
addr_t phys_addr(addr_t addr)
{
if (addr < _base || addr >= (_base + VM_SIZE))
return ~0UL;
int index = (addr - _base) / V_BLOCK_SIZE;
/* physical base of dataspace */
addr_t phys = _ds_phys[index];
if (!phys)
return ~0UL;
/* add offset */
phys += (addr - _base - (index * V_BLOCK_SIZE));
return phys;
}
addr_t virt_addr(addr_t phys)
{
for (unsigned i = 0; i < ELEMENTS; i++) {
if (_ds_cap[i].valid() &&
phys >= _ds_phys[i] && phys < _ds_phys[i] + P_BLOCK_SIZE)
return _base + i * V_BLOCK_SIZE + phys - _ds_phys[i];
}
PWRN("virt_addr(0x%lx) - no translation", phys);
return 0;
}
addr_t start() const { return _base; }
addr_t end() const { return _base + VM_SIZE - 1; }
};
class Lx_kit::Malloc : public Lx::Malloc
{
private:
enum {
SLAB_START_LOG2 = 3, /* 8 B */
SLAB_STOP_LOG2 = MAX_SIZE_LOG2,
NUM_SLABS = (SLAB_STOP_LOG2 - SLAB_START_LOG2) + 1,
};
typedef Genode::addr_t addr_t;
typedef Lx::Slab_alloc Slab_alloc;
typedef Lx::Slab_backend_alloc Slab_backend_alloc;
Slab_backend_alloc &_back_allocator;
Slab_alloc *_allocator[NUM_SLABS];
Genode::Cache_attribute _cached; /* cached or un-cached memory */
addr_t _start; /* VM region of this allocator */
addr_t _end;
/**
* Set 'value' at 'addr'
*/
void _set_at(addr_t addr, addr_t value) { *((addr_t *)addr) = value; }
/**
* Retrieve slab index belonging to given address
*/
unsigned _slab_index(Genode::addr_t **addr)
{
using namespace Genode;
/* get index */
addr_t index = *(*addr - 1);
/*
* If index large, we use aligned memory, retrieve beginning of slab entry
* and read index from there
*/
if (index > 32) {
*addr = (addr_t *)*(*addr - 1);
index = *(*addr - 1);
}
return index;
}
/**
* Get the originally requested size of the allocation
*/
size_t _get_orig_size(Genode::addr_t **addr)
{
using namespace Genode;
addr_t index = *(*addr - 1);
if (index > 32) {
*addr = (addr_t *) * (*addr - 1);
}
return *(*addr - 2);
}
public:
Malloc(Slab_backend_alloc &alloc, Genode::Cache_attribute cached)
:
_back_allocator(alloc), _cached(cached), _start(alloc.start()),
_end(alloc.end())
{
/* init slab allocators */
for (unsigned i = SLAB_START_LOG2; i <= SLAB_STOP_LOG2; i++)
_allocator[i - SLAB_START_LOG2] = new (Genode::env()->heap())
Slab_alloc(1U << i, alloc);
}
static Malloc & mem()
{
static Malloc inst(Slab_backend_alloc::mem(), Genode::CACHED);
return inst;
}
static Malloc & dma()
{
static Malloc inst(Slab_backend_alloc::dma(), Genode::UNCACHED);
return inst;
}
/**************************
** Lx::Malloc interface **
**************************/
void *alloc(Genode::size_t size, int align = 0, Genode::addr_t *phys = 0)
{
using namespace Genode;
/* save requested size */
size_t orig_size = size;
size += sizeof(addr_t);
/* += slab index + aligment size */
size += sizeof(addr_t) + (align > 2 ? (1 << align) : 0);
int msb = Genode::log2(size);
if (size > (1U << msb))
msb++;
if (size < (1U << SLAB_START_LOG2))
msb = SLAB_STOP_LOG2;
if (msb > SLAB_STOP_LOG2) {
PERR("Slab too large %u reqested %zu cached %d", 1U << msb, size, _cached);
return 0;
}
addr_t addr = _allocator[msb - SLAB_START_LOG2]->alloc();
if (!addr) {
PERR("Failed to get slab for %u", 1 << msb);
return 0;
}
_set_at(addr, orig_size);
addr += sizeof(addr_t);
_set_at(addr, msb - SLAB_START_LOG2);
addr += sizeof(addr_t);
if (align > 2) {
/* save */
addr_t ptr = addr;
addr_t align_val = (1U << align);
addr_t align_mask = align_val - 1;
/* align */
addr = (addr + align_val) & ~align_mask;
/* write start address before aligned address */
_set_at(addr - sizeof(addr_t), ptr);
}
if (phys)
*phys = _back_allocator.phys_addr(addr);
return (addr_t *)addr;
}
void free(void const *a)
{
using namespace Genode;
addr_t *addr = (addr_t *)a;
/* XXX changes addr */
unsigned nr = _slab_index(&addr);
/* we need to decrease addr by 2, orig_size and index come first */
_allocator[nr]->free((void *)(addr - 2));
}
void *alloc_large(size_t size)
{
void *addr;
if (!_back_allocator.alloc(size, &addr)) {
PERR("Large back end allocation failed (%zu bytes)", size);
return nullptr;
}
return addr;
}
void free_large(void *ptr)
{
_back_allocator.free(ptr);
}
size_t size(void const *a)
{
using namespace Genode;
addr_t *addr = (addr_t *)a;
/* XXX changes addr */
return _get_orig_size(&addr);
}
Genode::addr_t phys_addr(void *a) {
return _back_allocator.phys_addr((addr_t)a); }
Genode::addr_t virt_addr(Genode::addr_t phys) {
return _back_allocator.virt_addr(phys); }
bool inside(addr_t const addr) const { return (addr > _start) && (addr <= _end); }
};
/*******************************
** Lx::Malloc implementation **
*******************************/
/**
* Cached memory backend allocator
*/
Lx::Slab_backend_alloc &Lx::Slab_backend_alloc::mem() {
return Lx_kit::Slab_backend_alloc::mem(); }
/**
* DMA memory backend allocator
*/
Lx::Slab_backend_alloc &Lx::Slab_backend_alloc::dma() {
return Lx_kit::Slab_backend_alloc::dma(); }
/**
* Cached memory allocator
*/
Lx::Malloc &Lx::Malloc::mem() {
return Lx_kit::Malloc::mem(); }
/**
* DMA memory allocator
*/
Lx::Malloc &Lx::Malloc::dma() {
return Lx_kit::Malloc::dma(); }

View File

@ -0,0 +1,152 @@
/*
* \brief Representation of a locally-mapped MMIO range
* \author Norman Feske
* \date 2015-09-09
*/
/*
* Copyright (C) 2015-2016 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.
*/
/* Genode includes */
#include <os/attached_dataspace.h>
#include <io_mem_session/io_mem_session.h>
#include <rm_session/connection.h>
#include <region_map/client.h>
/* Linux emulation environment */
#include <lx_emul.h>
/* Linux emulation environment includes */
#include <lx_kit/internal/list.h>
#include <lx_kit/mapped_io_mem_range.h>
#include <lx_kit/pci_dev_registry.h>
#include <lx_kit/types.h>
namespace Lx_kit { class Mapped_io_mem_range; }
/**
* Representation of a locally-mapped MMIO range
*
* This class is supposed to be a private utility to support 'ioremap'.
*/
class Lx_kit::Mapped_io_mem_range : public Lx_kit::List<Mapped_io_mem_range>::Element,
public Genode::Rm_connection
{
private:
Genode::size_t const _size;
Genode::addr_t const _phys;
Genode::Region_map_client _region_map;
Genode::Attached_dataspace _ds;
Genode::addr_t const _virt;
public:
Mapped_io_mem_range(Genode::addr_t phys, Genode::size_t size,
Genode::Io_mem_dataspace_capability ds_cap,
Genode::addr_t offset)
: _size(size),
_phys(phys),
_region_map(Rm_connection::create(size)),
_ds(_region_map.dataspace()),
_virt((Genode::addr_t)_ds.local_addr<void>() | (phys &0xfffUL)) {
_region_map.attach_at(ds_cap, 0, size, offset); }
Genode::addr_t phys() const { return _phys; }
Genode::addr_t virt() const { return _virt; }
Genode::Dataspace_capability cap() const { return _ds.cap(); }
/**
* Return true if the mapped range contains the specified sub range
*/
bool phys_range(Genode::addr_t phys, Genode::size_t size) const
{
return (phys >= _phys) && (phys + size - 1 <= _phys + _size - 1);
}
/**
* Return true if the mapped range contains the specified sub range
*/
bool virt_range(Genode::addr_t virt, Genode::size_t size) const
{
return (virt >= _virt) && (virt + size - 1 <= _virt + _size - 1);
}
};
static Lx_kit::List<Lx_kit::Mapped_io_mem_range> ranges;
/********************************************
** Lx_kit::Mapped_io_mem_range implementation **
********************************************/
void *Lx::ioremap(addr_t phys_addr, unsigned long size,
Genode::Cache_attribute cache_attribute)
{
using namespace Genode;
/* search for the requested region within the already mapped ranges */
for (Lx_kit::Mapped_io_mem_range *r = ranges.first(); r; r = r->next()) {
if (r->phys_range(phys_addr, size)) {
void * const virt = (void *)(r->virt() + phys_addr - r->phys());
PLOG("ioremap: return sub range phys 0x%lx (size %lx) to virt 0x%lx",
(long)phys_addr, (long)size, (long)virt);
return virt;
}
}
addr_t offset = 0;
Io_mem_dataspace_capability ds_cap =
Lx::pci_dev_registry()->io_mem(phys_addr, cache_attribute,
size, offset);
if (!ds_cap.valid()) {
PERR("Failed to request I/O memory: [%lx,%lx)", phys_addr,
phys_addr + size);
return nullptr;
}
Lx_kit::Mapped_io_mem_range *io_mem =
new (env()->heap()) Lx_kit::Mapped_io_mem_range(phys_addr, size,
ds_cap, offset);
ranges.insert(io_mem);
PLOG("ioremap: mapped phys 0x%lx (size %lx) to virt 0x%lx",
(long)phys_addr, (long)size, (long)io_mem->virt());
return (void *)io_mem->virt();
}
void Lx::iounmap(volatile void * virt)
{
using namespace Genode;
/* search for the requested region within the already mapped ranges */
for (Lx_kit::Mapped_io_mem_range *r = ranges.first(); r; r = r->next()) {
if (r->virt() == (addr_t)virt) {
ranges.remove(r);
destroy(env()->heap(), r);
return;
}
}
}
Genode::Dataspace_capability
Lx::ioremap_lookup(Genode::addr_t virt_addr, Genode::size_t size)
{
/* search for the requested region within the already mapped ranges */
for (Lx_kit::Mapped_io_mem_range *r = ranges.first(); r; r = r->next())
if (r->virt_range(virt_addr, size))
return r->cap();
return Genode::Dataspace_capability();
}

View File

@ -0,0 +1,140 @@
/*
* \brief Backend allocator for DMA-capable memory
* \author Sebastian Sumpf
* \author Josef Soentgen
* \author Norman Feske
* \date 2014-10-10
*/
/*
* Copyright (C) 2014-2016 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.
*/
/* Genode includes */
#include <base/object_pool.h>
#include <base/env.h>
/* XXX only because of struct pci_dev */
#include <lx_emul.h>
/* Linux emulation environment includes */
#include <lx_kit/backend_alloc.h>
#include <lx_kit/pci_dev_registry.h>
namespace Lx_kit {
struct Memory_object_base;
struct Ram_object;
struct Dma_object;
Genode::Object_pool<Memory_object_base> memory_pool;
};
struct Lx_kit::Memory_object_base : Genode::Object_pool<Memory_object_base>::Entry
{
Memory_object_base(Genode::Ram_dataspace_capability cap)
: Genode::Object_pool<Memory_object_base>::Entry(cap) {}
virtual ~Memory_object_base() {};
virtual void free() = 0;
Genode::Ram_dataspace_capability ram_cap()
{
using namespace Genode;
return reinterpret_cap_cast<Ram_dataspace>(cap());
}
};
struct Lx_kit::Ram_object : Memory_object_base
{
Ram_object(Genode::Ram_dataspace_capability cap)
: Memory_object_base(cap) {}
void free() { Genode::env()->ram_session()->free(ram_cap()); }
};
struct Lx_kit::Dma_object : Memory_object_base
{
Dma_object(Genode::Ram_dataspace_capability cap)
: Memory_object_base(cap) {}
void free() { Lx::pci()->free_dma_buffer(ram_cap()); }
};
/*********************************
** Lx::Backend_alloc interface **
*********************************/
Genode::Ram_dataspace_capability
Lx::backend_alloc(Genode::addr_t size, Genode::Cache_attribute cached)
{
using namespace Genode;
using namespace Lx_kit;
Memory_object_base *o;
Genode::Ram_dataspace_capability cap;
if (cached == CACHED) {
cap = env()->ram_session()->alloc(size);
o = new (env()->heap()) Ram_object(cap);
} else {
size_t donate = size;
cap = retry<Platform::Session::Out_of_metadata>(
[&] () { return Lx::pci()->alloc_dma_buffer(size); },
[&] () {
char quota[32];
Genode::snprintf(quota, sizeof(quota), "ram_quota=%zd",
donate);
Genode::env()->parent()->upgrade(Lx::pci()->cap(), quota);
donate = donate * 2 > size ? 4096 : donate * 2;
});
o = new (env()->heap()) Dma_object(cap);
}
memory_pool.insert(o);
return cap;
}
void Lx::backend_free(Genode::Ram_dataspace_capability cap)
{
using namespace Genode;
using namespace Lx_kit;
Memory_object_base *object;
memory_pool.apply(cap, [&] (Memory_object_base *o) {
object = o;
if (!object)
return;
object->free();
memory_pool.remove(object);
});
destroy(env()->heap(), object);
}
/********************
** Pci singletons **
********************/
Platform::Connection *Lx::pci()
{
static Platform::Connection _pci;
return &_pci;
}
Lx::Pci_dev_registry *Lx::pci_dev_registry()
{
static Lx::Pci_dev_registry _pci_dev_registry;
return &_pci_dev_registry;
}

View File

@ -0,0 +1,445 @@
/*
* \brief Linux kit memory allocator
* \author Sebastian Sumpf
* \date 2016-04-20
*/
/*
* Copyright (C) 2016 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.
*/
/* Genode includes */
#include <base/console.h>
#include <base/printf.h>
/* local includes */
#include <lx_emul.h>
namespace Lx { class Console; }
extern "C" int stdout_write(const char *s);
static const bool verbose_console = false;
/**
* Format string command representation
*/
class Format_command
{
public:
enum Type { INT, UINT, STRING, CHAR, PTR, PERCENT, VA_FORMAT, MAC,
IPV4, INVALID };
enum Length { DEFAULT, LONG, SIZE_T, LONG_LONG };
private:
/**
* Read decimal value from string
*/
int decode_decimal(const char *str, int *consumed)
{
int res = 0;
while (1) {
char c = str[*consumed];
if (!c || c < '0' || c > '0' + 9)
return res;
res = (res * 10) + c - '0';
(*consumed)++;
}
}
public:
Type type = INVALID; /* format argument type */
Length length = DEFAULT; /* format argument length */
int padding = 0; /* min number of characters to print */
int base = 10; /* base of numeric arguments */
bool zeropad = false; /* pad with zero instead of space */
bool uppercase = false; /* use upper case for hex numbers */
bool prefix = false; /* prefix with 0x */
int consumed = 0; /* nb of consumed format string chars */
/**
* Constructor
*
* \param format begin of command in format string
*/
explicit Format_command(const char *format)
{
/* check for command begin and eat the character */
if (format[consumed] != '%') return;
if (!format[++consumed]) return;
/* check for %$x syntax */
prefix = (format[consumed] == '#');
if (prefix && !format[++consumed]) return;
/* heading zero indicates zero-padding */
zeropad = (format[consumed] == '0');
/* read decimal padding value */
padding = decode_decimal(format, &consumed);
if (!format[consumed]) return;
/* decode length */
switch (format[consumed]) {
case 'l':
{
/* long long ints are marked by a subsequenting 'l' character */
bool is_long_long = (format[consumed + 1] == 'l');
length = is_long_long ? LONG_LONG : LONG;
consumed += is_long_long ? 2 : 1;
break;
}
case 'z':
case 'Z':
length = SIZE_T;
consumed++;
break;
case 'p':
length = LONG;
break;
default: break;
}
if (!format[consumed]) return;
/* decode type */
switch (format[consumed]) {
case 'd':
case 'i': type = INT; base = 10; break;
case 'o': type = UINT; base = 8; break;
case 'u': type = UINT; base = 10; break;
case 'x': type = UINT; base = 16; break;
case 'X': type = UINT; base = 16; uppercase = 1; break;
case 'p': type = PTR; base = 16; break;
case 'c': type = CHAR; break;
case 's': type = STRING; break;
case '%': type = PERCENT; break;
case 0: return;
default: break;
}
/* eat type character */
consumed++;
if (type != PTR || !format[consumed])
return;
switch (format[consumed]) {
case 'V': type = VA_FORMAT; break;
case 'M': type = MAC; base = 16; padding = 2; break;
case 'I':
if (format[consumed + 1] != '4') break;
consumed++;
type = IPV4; base = 10;
break;
default: return;
}
consumed++;
}
int numeric()
{
return (type == INT || type == UINT || type == PTR);
}
};
/**
* Convert digit to ASCII value
*/
static char ascii(int digit, int uppercase = 0)
{
if (digit > 9)
return digit + (uppercase ? 'A' : 'a') - 10;
return digit + '0';
}
class Lx::Console
{
private:
enum { BUF_SIZE = 216 };
char _buf[BUF_SIZE + 1];
unsigned _idx = 0;
void _flush()
{
if (!_idx)
return;
_buf[_idx] = 0;
stdout_write(_buf);
_idx = 0;
}
/**
* Output signed value with the specified base
*/
template <typename T>
void _out_signed(T value, unsigned base)
{
/**
* for base 8, the number of digits is the number of value bytes times 3
* at a max, because 0xff is 0o377 and accumulating this implies a
* strictly decreasing factor
*/
char buf[sizeof(value)*3];
/* set flag if value is negative */
int neg = value < 0 ? 1 : 0;
/* get absolute value */
value = value < 0 ? -value : value;
int i = 0;
/* handle zero as special case */
if (value == 0)
buf[i++] = ascii(0);
/* fill buffer starting with the least significant digits */
else
for (; value > 0; value /= base)
buf[i++] = ascii(value % base);
/* add sign to buffer for negative values */
if (neg)
_out_char('-');
/* output buffer in reverse order */
for (; i--; )
_out_char(buf[i]);
}
/**
* Output unsigned value with the specified base and padding
*/
template <typename T>
void _out_unsigned(T value, unsigned base, int pad)
{
/**
* for base 8, the number of digits is the number of value bytes times 3
* at a max, because 0xff is 0o377 and accumulating this implies a
* strictly decreasing factor
*/
char buf[sizeof(value)*3];
int i = 0;
/* handle zero as special case */
if (value == 0) {
buf[i++] = ascii(0);
pad--;
}
/* fill buffer starting with the least significant digits */
for (; value > 0; value /= base, pad--)
buf[i++] = ascii(value % base);
/* add padding zeros */
for (; pad-- > 0; )
_out_char(ascii(0));
/* output buffer in reverse order */
for (; i--; )
_out_char(buf[i]);
}
protected:
void _out_char(char c)
{
if (c == '\n' || _idx == BUF_SIZE || c == 0)
_flush();
else
_buf[_idx++] = c;
}
void _out_string(const char *str)
{
if (str)
while (*str) _out_char(*str++);
else
_flush();
}
public:
static Console &c()
{
static Console _inst;
return _inst;
}
void vprintf(const char *format, va_list list)
{
while (*format) {
/* eat and output plain characters */
if (*format != '%') {
_out_char(*format++);
continue;
}
/* parse format argument descriptor */
Format_command cmd(format);
/* read numeric argument from va_list */
long long numeric_arg = 0;
if (cmd.numeric()) {
switch (cmd.length) {
case Format_command::LONG_LONG:
numeric_arg = va_arg(list, long long);
break;
case Format_command::LONG:
numeric_arg = (cmd.type == Format_command::UINT) ?
(long long)va_arg(list, unsigned long) : va_arg(list, long);
break;
case Format_command::SIZE_T:
numeric_arg = va_arg(list, size_t);
break;
case Format_command::DEFAULT:
numeric_arg = (cmd.type == Format_command::UINT) ?
(long long)va_arg(list, unsigned int) : va_arg(list, int);
break;
}
}
/* call type-specific output routines */
switch (cmd.type) {
case Format_command::INT:
if (cmd.length == Format_command::LONG_LONG)
_out_signed<long long>(numeric_arg, cmd.base);
else
_out_signed<long>(numeric_arg, cmd.base);
break;
case Format_command::UINT:
if (cmd.prefix && cmd.base == 16)
_out_string("0x");
if (cmd.length == Format_command::LONG_LONG) {
_out_unsigned<unsigned long long>(numeric_arg, cmd.base, cmd.padding);
break;
}
/* fall through */
case Format_command::PTR:
_out_unsigned<unsigned long>(numeric_arg, cmd.base, cmd.padding);
break;
case Format_command::CHAR:
_out_char(va_arg(list, int));
break;
case Format_command::STRING:
_out_string(va_arg(list, const char *));
break;
case Format_command::PERCENT:
_out_char('%');
break;
case Format_command::VA_FORMAT: /* %pV */
{
va_list va;
va_format *vf = va_arg(list, va_format *);
va_copy(va, *vf->va);
vprintf(vf->fmt, va);
va_end(va);
}
break;
case Format_command::MAC: /* %pM */
{
unsigned char const *mac = va_arg(list, unsigned char const *);
for (int i = 0; i < 6; i++) {
if (i) _out_char(':');
_out_unsigned<unsigned char>(mac[i], cmd.base, cmd.padding);
}
break;
}
case Format_command::IPV4: /* %pI4 */
{
unsigned char const *ip = va_arg(list, unsigned char const *);
for (int i = 0; i < 4; i++) {
if (i) _out_char('.');
_out_unsigned<unsigned char>(ip[i], cmd.base, cmd.padding);
}
}
break;
case Format_command::INVALID:
_out_string("<warning: unsupported format string argument>");
/* consume the argument of the unsupported command */
va_arg(list, long);
break;
}
/* proceed with format string after command */
format += cmd.consumed;
}
}
};
void lx_printf(char const *fmt, ...)
{
if (verbose_console)
PDBG("[%p] %s", __builtin_return_address(0), fmt);
va_list va;
va_start(va, fmt);
Lx::Console::c().vprintf(fmt, va);
va_end(va);
}
void lx_vprintf(char const *fmt, va_list va) {
Lx::Console::c().vprintf(fmt, va); }

View File

@ -0,0 +1,226 @@
/*
* \brief Scheduler for executing Lx::Task objects
* \author Sebastian Sumpf
* \author Josef Soentgen
* \author Norman Feske
* \date 2014-10-10
*/
/*
* Copyright (C) 2014-2016 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.
*/
/* Genode includes */
#include <base/env.h>
#include <base/lock.h>
#include <base/printf.h>
#include <base/sleep.h>
#include <base/thread.h>
#include <timer_session/connection.h>
/* Linux emulation environment includes */
#include <lx_kit/internal/debug.h>
#include <lx_kit/scheduler.h>
#include <lx_kit/timer.h>
namespace Lx_kit {
class Scheduler;
}
class Lx_kit::Scheduler : public Lx::Scheduler
{
private:
bool verbose = false;
Lx_kit::List<Lx::Task> _present_list;
Genode::Lock _present_list_mutex;
Lx::Task *_current = nullptr; /* currently scheduled task */
bool _run_task(Lx::Task *);
/*
* Support for logging
*/
static inline char const *_ansi_esc_reset() { return "\033[00m"; }
static inline char const *_ansi_esc_black() { return "\033[30m"; }
static inline char const *_ansi_esc_red() { return "\033[31m"; }
static inline char const *_ansi_esc_yellow() { return "\033[33m"; }
static inline char const *_state_color(Lx::Task::State state)
{
switch (state) {
case Lx::Task::STATE_INIT: return _ansi_esc_reset();
case Lx::Task::STATE_RUNNING: return _ansi_esc_red();
case Lx::Task::STATE_BLOCKED: return _ansi_esc_yellow();
case Lx::Task::STATE_MUTEX_BLOCKED: return _ansi_esc_yellow();
case Lx::Task::STATE_WAIT_BLOCKED: return _ansi_esc_yellow();
}
return _ansi_esc_black();
}
struct Logger : Genode::Thread_deprecated<0x4000>
{
Timer::Connection _timer;
Lx::Scheduler &_scheduler;
unsigned const _interval;
Logger(Lx::Scheduler &scheduler, unsigned interval_seconds)
:
Genode::Thread_deprecated<0x4000>("logger"),
_scheduler(scheduler), _interval(interval_seconds)
{
start();
}
void entry()
{
PWRN("Scheduler::Logger is up");
_timer.msleep(1000 * _interval);
while (true) {
_scheduler.log_state("LOGGER");
_timer.msleep(2000);
}
}
};
public:
Scheduler()
{
if (verbose)
new (Genode::env()->heap()) Logger(*this, 10);
}
/*****************************
** Lx::Scheduler interface **
*****************************/
Lx::Task *current() override
{
if (!_current) {
PERR("BUG: _current is zero!");
Genode::sleep_forever();
}
return _current;
}
bool active() const override {
return _current != nullptr; }
void add(Lx::Task *task) override
{
Lx::Task *p = _present_list.first();
for ( ; p; p = p->next()) {
if (p->priority() <= task->priority()) {
_present_list.insert_before(task, p);
break;
}
}
if (!p)
_present_list.append(task);
}
void remove(Lx::Task *task) override
{
_present_list.remove(task);
}
void schedule() override
{
bool at_least_one = false;
/*
* Iterate over all tasks and run first runnable.
*
* (1) If one runnable tasks was run start over from beginning of
* list.
*
* (2) If no task is runnable quit scheduling (break endless
* loop).
*/
while (true) {
/* update jiffies before running task */
Lx::timer_update_jiffies();
bool was_run = false;
for (Lx::Task *t = _present_list.first(); t; t = t->next()) {
/* update current before running task */
_current = t;
if ((was_run = t->run())) {
at_least_one = true;
break;
}
}
if (!was_run)
break;
}
if (!at_least_one) {
PWRN("schedule() called without runnable tasks");
log_state("SCHEDULE");
}
/* clear current as no task is running */
_current = nullptr;
}
void log_state(char const *prefix) override
{
unsigned i;
Lx::Task *t;
for (i = 0, t = _present_list.first(); t; t = t->next(), ++i) {
Genode::printf("%s [%u] prio: %u state: %s%u%s %s\n",
prefix, i, t->priority(), _state_color(t->state()),
t->state(), _ansi_esc_reset(), t->name());
}
}
};
/*****************************
** Lx::Task implementation **
*****************************/
Lx::Task::Task(void (*func)(void*), void *arg, char const *name,
Priority priority, Scheduler &scheduler)
:
_priority(priority), _scheduler(scheduler),
_func(func), _arg(arg), _name(name)
{
scheduler.add(this);
if (verbose)
PDBG("name: '%s' func: %p arg: %p prio: %u t: %p", name, func, arg, priority, this);
}
Lx::Task::~Task()
{
_scheduler.remove(this);
if (_stack)
Genode::Thread::myself()->free_secondary_stack(_stack);
}
/**********************************
** Lx::Scheduler implementation **
**********************************/
Lx::Scheduler &Lx::scheduler()
{
static Lx_kit::Scheduler inst;
return inst;
}

View File

@ -0,0 +1,91 @@
/* $NetBSD: _setjmp.S,v 1.5 2003/04/05 23:08:51 bjh21 Exp $ */
/*
* Copyright (c) 1997 Mark Brinicombe
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Mark Brinicombe
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
//#include <machine/asm.h>
__FBSDID("$FreeBSD$");
/*
* C library -- _setjmp, _longjmp
*
* _longjmp(a,v)
* will generate a "return(v)" from the last call to
* _setjmp(a)
* by restoring registers from the stack.
* The previous signal state is NOT restored.
*
* Note: r0 is the return value
* r1-r3 are scratch registers in functions
*/
ENTRY(_setjmp)
ldr r1, .L_setjmp_magic
str r1, [r0], #4
/* SOFTFP */
add r0, r0, #52
/* Store integer registers */
stmia r0, {r4-r14}
mov r0, #0x00000000
RET
.L_setjmp_magic:
.word _JB_MAGIC__SETJMP
WEAK_ALIAS(___longjmp, _longjmp)
ENTRY(_longjmp)
ldr r2, .L_setjmp_magic
ldr r3, [r0], #4
teq r2, r3
bne botch
/* SOFTFP */
add r0, r0, #52
/* Restore integer registers */
ldmia r0, {r4-r14}
/* Validate sp and r14 */
teq sp, #0
teqne r14, #0
beq botch
/* Set return value */
mov r0, r1
teq r0, #0x00000000
moveq r0, #0x00000001
RET
/* validation failed, die die die. */
botch:
b .

View File

@ -0,0 +1,79 @@
/*-
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* William Jolitz.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#if defined(LIBC_SCCS) && !defined(lint)
.asciz "@(#)_setjmp.s 5.1 (Berkeley) 4/23/90"
#endif /* LIBC_SCCS and not lint */
//#include <machine/asm.h>
/*
* C library -- _setjmp, _longjmp
*
* _longjmp(a,v)
* will generate a "return(v)" from the last call to
* _setjmp(a)
* by restoring registers from the environment 'a'.
* The previous signal state is NOT restored.
*/
.text; .p2align 2,0x90
.globl _setjmp; .type _setjmp,@function; _setjmp:
movl 4(%esp),%eax
movl 0(%esp),%edx
movl %edx, 0(%eax) /* rta */
movl %ebx, 4(%eax)
movl %esp, 8(%eax)
movl %ebp,12(%eax)
movl %esi,16(%eax)
movl %edi,20(%eax)
fnstcw 24(%eax)
xorl %eax,%eax
ret
.size _setjmp, . - _setjmp
.text; .p2align 2,0x90
.globl _longjmp; .type _longjmp,@function; _longjmp:
movl 4(%esp),%edx
movl 8(%esp),%eax
movl 0(%edx),%ecx
movl 4(%edx),%ebx
movl 8(%edx),%esp
movl 12(%edx),%ebp
movl 16(%edx),%esi
movl 20(%edx),%edi
fldcw 24(%edx)
testl %eax,%eax
jnz 1f
incl %eax
1: movl %ecx,0(%esp)
ret
.size _longjmp, . - _longjmp

View File

@ -0,0 +1,93 @@
/*-
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* William Jolitz.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#if defined(LIBC_SCCS) && !defined(lint)
.asciz "@(#)_setjmp.s 5.1 (Berkeley) 4/23/90"
#endif /* LIBC_SCCS and not lint */
//#include <machine/asm.h>
/*
* C library -- _setjmp, _longjmp
*
* _longjmp(a,v)
* will generate a "return(v)" from the last call to
* _setjmp(a)
* by restoring registers from the environment 'a'.
* The previous signal state is NOT restored.
*/
.text; .p2align 4,0x90
.globl _setjmp; .type _setjmp,@function; _setjmp:
movq %rdi,%rax
movq 0(%rsp),%rdx /* retval */
movq %rdx, 0(%rax) /* 0; retval */
movq %rbx, 8(%rax) /* 1; rbx */
movq %rsp,16(%rax) /* 2; rsp */
movq %rbp,24(%rax) /* 3; rbp */
movq %r12,32(%rax) /* 4; r12 */
movq %r13,40(%rax) /* 5; r13 */
movq %r14,48(%rax) /* 6; r14 */
movq %r15,56(%rax) /* 7; r15 */
fnstcw 64(%rax) /* 8; fpu cw */
stmxcsr 68(%rax) /* and mxcsr */
xorq %rax,%rax
ret
.size _setjmp, . - _setjmp
.text; .p2align 4,0x90
.globl _longjmp; .type _longjmp,@function; _longjmp:
movq %rdi,%rdx
/* Restore the mxcsr, but leave exception flags intact. */
stmxcsr -4(%rsp)
movl 68(%rdx),%eax
andl $0xffffffc0,%eax
movl -4(%rsp),%edi
andl $0x3f,%edi
xorl %eax,%edi
movl %edi,-4(%rsp)
ldmxcsr -4(%rsp)
movq %rsi,%rax /* retval */
movq 0(%rdx),%rcx
movq 8(%rdx),%rbx
movq 16(%rdx),%rsp
movq 24(%rdx),%rbp
movq 32(%rdx),%r12
movq 40(%rdx),%r13
movq 48(%rdx),%r14
movq 56(%rdx),%r15
fldcw 64(%rdx)
testq %rax,%rax
jnz 1f
incq %rax
1: movq %rcx,0(%rsp)
ret
.size _longjmp, . - _longjmp

View File

@ -0,0 +1,307 @@
/*
* \brief Timer
* \author Sebastian Sumpf
* \author Josef Soentgen
* \author Norman Feske
* \date 2014-10-10
*/
/*
* Copyright (C) 2014-2016 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.
*/
/* Genode includes */
#include <os/server.h>
#include <base/tslab.h>
#include <timer_session/connection.h>
/* Linux kit includes */
#include <lx_kit/internal/list.h>
#include <lx_kit/scheduler.h>
/* Linux emulation environment includes */
#include <lx_emul.h>
#include <lx_kit/timer.h>
namespace Lx_kit { class Timer; }
class Lx_kit::Timer : public Lx::Timer
{
public:
/**
* Context encapsulates a regular linux timer_list
*/
struct Context : public Lx_kit::List<Context>::Element
{
enum { INVALID_TIMEOUT = ~0UL };
Type type;
void *timer;
bool pending { false };
unsigned long timeout { INVALID_TIMEOUT }; /* absolute in jiffies */
bool programmed { false };
Context(struct timer_list *timer) : type(LIST), timer(timer) { }
Context(struct hrtimer *timer) : type(HR), timer(timer) { }
void expires(unsigned long e)
{
if (type == LIST)
static_cast<timer_list *>(timer)->expires = e;
}
void function()
{
switch (type) {
case LIST:
{
timer_list *t = static_cast<timer_list *>(timer);
if (t->function)
t->function(t->data);
}
break;
case HR:
{
hrtimer *t = static_cast<hrtimer *>(timer);
if (t->function)
t->function(t);
}
break;
}
}
};
private:
unsigned long &_jiffies;
::Timer::Connection _timer_conn;
Lx_kit::List<Context> _list;
Lx::Task _timer_task;
Genode::Signal_rpc_member<Lx_kit::Timer> _dispatcher;
Genode::Tslab<Context, 32 * sizeof(Context)> _timer_alloc;
/**
* Lookup local timer
*/
Context *_find_context(void const *timer)
{
for (Context *c = _list.first(); c; c = c->next())
if (c->timer == timer)
return c;
return 0;
}
/**
* Program the first timer in the list
*
* The first timer is programmed if the 'programmed' flag was not set
* before. The second timer is flagged as not programmed as
* 'Timer::trigger_once' invalidates former registered one-shot
* timeouts.
*/
void _program_first_timer()
{
Context *ctx = _list.first();
if (!ctx)
return;
if (ctx->programmed)
return;
/* calculate relative microseconds for trigger */
unsigned long us = ctx->timeout > _jiffies ?
jiffies_to_msecs(ctx->timeout - _jiffies) * 1000 : 0;
_timer_conn.trigger_once(us);
ctx->programmed = true;
/* possibly programmed successor must be reprogrammed later */
if (Context *next = ctx->next())
next->programmed = false;
}
/**
* Schedule timer
*
* Add the context to the scheduling list depending on its timeout
* and reprogram the first timer.
*/
void _schedule_timer(Context *ctx, unsigned long expires)
{
_list.remove(ctx);
ctx->timeout = expires;
ctx->pending = true;
ctx->programmed = false;
/*
* Also write the timeout value to the expires field in
* struct timer_list because the wireless stack checks
* it directly.
*/
ctx->expires(expires);
Context *c;
for (c = _list.first(); c; c = c->next())
if (ctx->timeout <= c->timeout)
break;
_list.insert_before(ctx, c);
_program_first_timer();
}
/**
* Handle trigger_once signal
*/
void _handle(unsigned)
{
_timer_task.unblock();
Lx::scheduler().schedule();
}
public:
/**
* Constructor
*/
Timer(Server::Entrypoint &ep, unsigned long &jiffies)
:
_jiffies(jiffies),
_timer_task(Timer::run_timer, reinterpret_cast<void*>(this),
"timer", Lx::Task::PRIORITY_2, Lx::scheduler()),
_dispatcher(ep, *this, &Lx_kit::Timer::_handle),
_timer_alloc(Genode::env()->heap())
{
_timer_conn.sigh(_dispatcher);
}
Context* first() { return _list.first(); }
unsigned long jiffies() const { return _jiffies; }
static void run_timer(void *p)
{
Timer &t = *reinterpret_cast<Timer*>(p);
while (1) {
Lx::scheduler().current()->block_and_schedule();
Lx_kit::Timer::Context *ctx = t.first();
if (!ctx || ctx->timeout > t.jiffies())
continue;;
ctx->pending = false;
ctx->function();
if (!ctx->pending)
t.del(ctx->timer);
t.schedule_next();
}
}
/*************************
** Lx::Timer interface **
*************************/
void add(void *timer, Type type)
{
Context *t = nullptr;
if (type == HR)
t = new (&_timer_alloc) Context(static_cast<hrtimer *>(timer));
else
t = new (&_timer_alloc) Context(static_cast<timer_list *>(timer));
_list.append(t);
}
int del(void *timer)
{
Context *ctx = _find_context(timer);
/**
* If the timer expired it was already cleaned up after its
* execution.
*/
if (!ctx)
return 0;
int rv = ctx->timeout != Context::INVALID_TIMEOUT ? 1 : 0;
_list.remove(ctx);
destroy(&_timer_alloc, ctx);
return rv;
}
int schedule(void *timer, unsigned long expires)
{
Context *ctx = _find_context(timer);
if (!ctx) {
PERR("schedule unknown timer %p", timer);
return -1; /* XXX better use 0 as rv? */
}
/*
* If timer was already active return 1, otherwise 0. The return
* value is needed by mod_timer().
*/
int rv = ctx->timeout != Context::INVALID_TIMEOUT ? 1 : 0;
_schedule_timer(ctx, expires);
return rv;
}
void schedule_next() { _program_first_timer(); }
/**
* Check if the timer is currently pending
*/
bool pending(void const *timer)
{
Context *ctx = _find_context(timer);
if (!ctx) {
return false;
}
return ctx->pending;
}
bool find(void const *timer) const
{
for (Context const *c = _list.first(); c; c = c->next())
if (c->timer == timer)
return true;
return false;
}
void update_jiffies() {
_jiffies = msecs_to_jiffies(_timer_conn.elapsed_ms()); }
};
/******************************
** Lx::Timer implementation **
******************************/
Lx::Timer &Lx::timer(Server::Entrypoint *ep, unsigned long *jiffies)
{
static Lx_kit::Timer inst(*ep, *jiffies);
return inst;
}
void Lx::timer_update_jiffies()
{
timer().update_jiffies();
}

View File

@ -0,0 +1,172 @@
/*
* \brief Work queue implementation
* \author Josef Soentgen
* \author Stefan Kalkowski
* \date 2015-10-26
*/
/*
* Copyright (C) 2015-2016 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.
*/
/* Genode includes */
#include <base/tslab.h>
#include <lx_kit/internal/list.h>
#include <lx_kit/scheduler.h>
/* Linux emulation environment includes */
#include <lx_emul.h>
#include <lx_kit/work.h>
namespace Lx_kit { class Work; }
class Lx_kit::Work : public Lx::Work
{
public:
/**
* Context encapsulates a normal work item
*/
struct Context : public Lx_kit::List<Context>::Element
{
void *work;
enum Type { NORMAL, DELAYED, TASKLET } type;
void exec() {
switch (type) {
case NORMAL:
{
work_struct *w = static_cast<work_struct *>(work);
w->func(w);
}
break;
case DELAYED:
{
delayed_work *w = static_cast<delayed_work *>(work);
w->work.func(&(w)->work);
}
break;
case TASKLET:
{
tasklet_struct *tasklet = static_cast<tasklet_struct *>(work);
tasklet->func(tasklet->data);
}
break;
}
}
Context(delayed_work *w) : work(w), type(DELAYED) { }
Context(work_struct *w) : work(w), type(NORMAL) { }
Context(tasklet_struct *w) : work(w), type(TASKLET) { }
};
private:
Lx::Task _task;
Lx_kit::List<Context> _list;
Genode::Tslab<Context, 64 * sizeof(Context)> _work_alloc;
/**
* Schedule work item
*/
template <typename WORK>
void _schedule(WORK *work)
{
Context *c = new (&_work_alloc) Context(work);
_list.append(c);
}
public:
Work(Genode::Allocator &alloc, char const *name = "work_queue")
: _task(Work::run_work, reinterpret_cast<void*>(this), name,
Lx::Task::PRIORITY_2, Lx::scheduler()),
_work_alloc(&alloc) { }
/**
* Execute all available work items
*/
void exec()
{
while (Context *c = _list.first()) {
_list.remove(c);
c->exec();
destroy(&_work_alloc, c);
}
}
static void run_work(void *wq)
{
Work *work_queue = reinterpret_cast<Work*>(wq);
while (1) {
work_queue->exec();
Lx::scheduler().current()->block_and_schedule();
}
}
/************************
** Lx::Work interface **
************************/
void unblock() { _task.unblock(); }
void schedule(struct work_struct *work) {
_schedule(work); }
void schedule_delayed(struct delayed_work *work,
unsigned long /*delay*/) {
_schedule(work); }
void schedule_tasklet(struct tasklet_struct *tasklet) {
_schedule(tasklet); }
bool cancel_work(struct work_struct *work, bool sync = false)
{
for (Context *c = _list.first(); c; c = c->next()) {
if (c->work == work) {
if (sync)
c->exec();
_list.remove(c);
destroy(&_work_alloc, c);
return true;
}
}
return false;
}
char const *task_name() override { return _task.name(); }
};
/*****************************
** Lx::Work implementation **
*****************************/
Lx::Work & Lx::Work::work_queue(Genode::Allocator *alloc)
{
static Lx_kit::Work inst(*alloc);
return inst;
}
Lx::Work * Lx::Work::alloc_work_queue(Genode::Allocator *alloc, char const *name)
{
Lx::Work *work = new (alloc) Lx_kit::Work(*alloc, name);
return work;
}
void Lx::Work::free_work_queue(Lx::Work *w)
{
PERR("%s: IMPLEMENT ME", __func__);
}