Move repositories to 'repos/' subdirectory

This patch changes the top-level directory layout as a preparatory
step for improving the tools for managing 3rd-party source codes.
The rationale is described in the issue referenced below.

Issue #1082
This commit is contained in:
Norman Feske
2014-05-07 11:48:19 +02:00
parent 1f9890d635
commit ca971bbfd8
3943 changed files with 454 additions and 430 deletions

View File

@ -0,0 +1,569 @@
/*
* \brief Data structure for storing sparse blocks
* \author Norman Feske
* \author Stefan Kalkowski
* \date 2014-01-06
*
* Note: originally taken from ram_fs server, and adapted to cache needs
*/
/*
* Copyright (C) 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.
*/
#ifndef _CHUNK_H_
#define _CHUNK_H_
/* Genode includes */
#include <util/noncopyable.h>
#include <base/allocator.h>
#include <base/exception.h>
#include <util/list.h>
#include <util/string.h>
namespace Cache {
typedef Genode::uint64_t offset_t;
typedef Genode::uint64_t size_t;
/**
* Common base class of both 'Chunk' and 'Chunk_index'
*/
class Chunk_base : Genode::Noncopyable
{
public:
struct Range_exception : Genode::Exception
{
offset_t off;
size_t size;
Range_exception(offset_t o, size_t s) : off(o), size(s) {}
};
typedef Range_exception Index_out_of_range;
typedef Range_exception Range_incomplete;
protected:
offset_t const _base_offset;
size_t _num_entries; /* corresponds to last used entry */
Chunk_base *_parent;
/**
* Test if specified range lies within the chunk
*/
void assert_valid_range(offset_t start, size_t len,
size_t chunk_size) const
{
if (start < _base_offset)
throw Index_out_of_range(start, len);
if (start + len > _base_offset + chunk_size)
throw Index_out_of_range(start, len);
}
Chunk_base(offset_t base_offset, Chunk_base *p)
: _base_offset(base_offset), _num_entries(0), _parent(p) { }
/**
* Construct zero chunk
*/
Chunk_base() : _base_offset(0), _num_entries(0), _parent(0) { }
public:
/**
* Return absolute base offset of chunk in bytes
*/
offset_t base_offset() const { return _base_offset; }
/**
* Return true if chunk has no allocated sub chunks
*/
bool empty() const { return _num_entries == 0; }
virtual void free(size_t, offset_t) = 0;
};
/**
* Chunk of bytes used as leaf in hierarchy of chunk indices
*/
template <unsigned CHUNK_SIZE, typename POLICY>
class Chunk : public Chunk_base,
public POLICY::Element
{
private:
char _data[CHUNK_SIZE];
unsigned _writes;
public:
typedef Range_exception Dirty_chunk;
static constexpr size_t SIZE = CHUNK_SIZE;
/**
* Construct byte chunk
*
* \param base_offset absolute offset of chunk in bytes
*
* The first argument is unused. Its mere purpose is to make the
* signature of the constructor compatible to the constructor
* of 'Chunk_index'.
*/
Chunk(Genode::Allocator &, offset_t base_offset, Chunk_base *p)
: Chunk_base(base_offset, p), _writes(0) { }
/**
* Construct zero chunk
*/
Chunk() : _writes(0) { }
/**
* Return number of used entries
*
* The returned value corresponds to the index of the last used
* entry + 1. It does not correlate to the number of actually
* allocated entries (there may be ranges of zero blocks).
*/
size_t used_size() const { return _num_entries; }
void write(char const *src, size_t len, offset_t seek_offset)
{
assert_valid_range(seek_offset, len, SIZE);
POLICY::write(this);
/* offset relative to this chunk */
offset_t const local_offset = seek_offset - base_offset();
Genode::memcpy(&_data[local_offset], src, len);
_num_entries = Genode::max(_num_entries, local_offset + len);
_writes++;
}
void read(char *dst, size_t len, offset_t seek_offset) const
{
assert_valid_range(seek_offset, len, SIZE);
POLICY::read(this);
Genode::memcpy(dst, &_data[seek_offset - base_offset()], len);
}
void stat(size_t len, offset_t seek_offset) const
{
assert_valid_range(seek_offset, len, SIZE);
if (_writes == 0)
throw Range_incomplete(base_offset(), SIZE);
}
void sync(size_t len, offset_t seek_offset)
{
if (_writes > 1) {
POLICY::sync(this, (char*)_data);
_writes = 1;
}
}
void alloc(size_t len, offset_t seek_offset) { }
void truncate(size_t size)
{
assert_valid_range(size, 0, SIZE);
/*
* Offset of the first free position (relative to the beginning
* this chunk).
*/
offset_t const local_offset = size - base_offset();
if (local_offset >= _num_entries)
return;
Genode::memset(&_data[local_offset], 0, _num_entries - local_offset);
_num_entries = local_offset;
}
void free(size_t, offset_t)
{
if (_writes > 1) throw Dirty_chunk(_base_offset, SIZE);
_num_entries = 0;
if (_parent) _parent->free(SIZE, _base_offset);
}
};
template <unsigned NUM_ENTRIES, typename ENTRY_TYPE, typename POLICY>
class Chunk_index : public Chunk_base
{
public:
typedef ENTRY_TYPE Entry;
static constexpr size_t ENTRY_SIZE = ENTRY_TYPE::SIZE;
static constexpr size_t SIZE = ENTRY_SIZE*NUM_ENTRIES;
private:
Genode::Allocator &_alloc;
Entry * _entries[NUM_ENTRIES];
/**
* Return instance of a zero sub chunk
*/
static Entry &_zero_chunk()
{
static Entry zero_chunk;
return zero_chunk;
}
/**
* Return sub chunk at given index
*
* If there is no sub chunk at the specified index, this function
* transparently allocates one. Hence, the returned sub chunk
* is ready to be written to.
*/
Entry &_alloc_entry(unsigned index)
{
if (index >= NUM_ENTRIES)
throw Index_out_of_range(base_offset() + index*ENTRY_SIZE,
ENTRY_SIZE);
if (_entries[index])
return *_entries[index];
offset_t entry_offset = base_offset() + index*ENTRY_SIZE;
for (;;) {
try {
_entries[index] = new (&_alloc)
Entry(_alloc, entry_offset, this);
break;
} catch(Genode::Allocator::Out_of_memory) {
POLICY::flush(sizeof(Entry));
}
}
_num_entries = Genode::max(_num_entries, index + 1);
return *_entries[index];
}
/**
* Return sub chunk at given index
*/
Entry &_entry(unsigned index) const
{
if (index >= NUM_ENTRIES)
throw Index_out_of_range(base_offset() + index*ENTRY_SIZE,
ENTRY_SIZE);
if (_entries[index])
return *_entries[index];
throw Range_incomplete(base_offset() + index*ENTRY_SIZE,
ENTRY_SIZE);
}
/**
* Return sub chunk at given index (for syncing only)
*
* This function transparently provides a zero sub chunk for any
* index that is not populated by a real chunk.
*/
Entry &_entry_for_syncing(unsigned index) const
{
if (index >= NUM_ENTRIES)
throw Index_out_of_range(base_offset() + index*ENTRY_SIZE,
ENTRY_SIZE);
if (_entries[index])
return *_entries[index];
return _zero_chunk();
}
/**
* Return index of entry located at specified byte offset
*
* The caller of this function must make sure that the offset
* parameter is within the bounds of the chunk.
*/
unsigned _index_by_offset(offset_t offset) const
{
return (offset - base_offset()) / ENTRY_SIZE;
}
/**
* Apply operation 'func' to a range of entries
*/
template <typename THIS, typename DATA, typename FUNC>
static void _range_op(THIS &obj, DATA *data, size_t len,
offset_t seek_offset, FUNC const &func)
{
/*
* Depending on whether this function is called for reading
* (const function) or writing (non-const function), the
* operand type is const or non-const Entry. The correct type
* is embedded as a trait in the 'FUNC' functor type.
*/
typedef typename FUNC::Entry Const_qualified_entry;
obj.assert_valid_range(seek_offset, len, SIZE);
while (len > 0) {
unsigned const index = obj._index_by_offset(seek_offset);
Const_qualified_entry &entry = FUNC::lookup(obj, index);
/*
* Calculate byte offset relative to the chunk
*
* We cannot use 'entry.base_offset()' for this calculation
* because in the const case, the lookup might return a
* zero chunk, which has no defined base offset. Therefore,
* we calculate the base offset via index*ENTRY_SIZE.
*/
offset_t const local_seek_offset =
seek_offset - obj.base_offset() - index*ENTRY_SIZE;
/* available capacity at 'entry' starting at seek offset */
offset_t const capacity = ENTRY_SIZE - local_seek_offset;
size_t const curr_len = Genode::min(len, capacity);
/* apply functor (read or write) to entry */
func(entry, data, curr_len, seek_offset);
/* advance to next entry */
len -= curr_len;
data += curr_len;
seek_offset += curr_len;
}
}
struct Alloc_func
{
typedef ENTRY_TYPE Entry;
static Entry &lookup(Chunk_index &chunk, unsigned i) {
return chunk._alloc_entry(i); }
void operator () (Entry &entry, char const *src, size_t len,
offset_t seek_offset) const
{
entry.alloc(len, seek_offset);
}
};
struct Write_func
{
typedef ENTRY_TYPE Entry;
static Entry &lookup(Chunk_index &chunk, unsigned i) {
return chunk._entry(i); }
void operator () (Entry &entry, char const *src, size_t len,
offset_t seek_offset) const
{
entry.write(src, len, seek_offset);
}
};
struct Read_func
{
typedef ENTRY_TYPE const Entry;
static Entry &lookup(Chunk_index const &chunk, unsigned i) {
return chunk._entry(i); }
void operator () (Entry &entry, char *dst, size_t len,
offset_t seek_offset) const {
entry.read(dst, len, seek_offset); }
};
struct Stat_func
{
typedef ENTRY_TYPE const Entry;
static Entry &lookup(Chunk_index const &chunk, unsigned i) {
return chunk._entry(i); }
void operator () (Entry &entry, char*, size_t len,
offset_t seek_offset) const {
entry.stat(len, seek_offset); }
};
struct Sync_func
{
typedef ENTRY_TYPE Entry;
static Entry &lookup(Chunk_index const &chunk, unsigned i) {
return chunk._entry_for_syncing(i); }
void operator () (Entry &entry, char*, size_t len,
offset_t seek_offset) const
{
entry.sync(len, seek_offset);
}
};
void _init_entries()
{
for (unsigned i = 0; i < NUM_ENTRIES; i++)
_entries[i] = 0;
}
void _destroy_entry(unsigned i)
{
if (_entries[i] && (i < _num_entries)) {
Genode::destroy(&_alloc, _entries[i]);
_entries[i] = 0;
}
}
public:
/**
* Constructor
*
* \param alloc allocator to use for allocating sub-chunk
* indices and chunks
* \param base_offset absolute offset of the chunk in bytes
*/
Chunk_index(Genode::Allocator &alloc, offset_t base_offset,
Chunk_base *p = 0)
: Chunk_base(base_offset, p), _alloc(alloc) { _init_entries(); }
/**
* Construct zero chunk
*/
Chunk_index() : _alloc(*(Genode::Allocator *)0) { }
/**
* Destructor
*/
~Chunk_index()
{
for (unsigned i = 0; i < NUM_ENTRIES; i++)
_destroy_entry(i);
}
/**
* Return size of chunk in bytes
*
* The returned value corresponds to the position after the highest
* offset that was written to.
*/
size_t used_size() const
{
if (_num_entries == 0)
return 0;
/* size of entries that lie completely within the used range */
size_t const size_whole_entries = ENTRY_SIZE*(_num_entries - 1);
Entry *last_entry = _entries[_num_entries - 1];
if (!last_entry)
return size_whole_entries;
return size_whole_entries + last_entry->used_size();
}
/**
* Write data to chunk
*/
void write(char const *src, size_t len, offset_t seek_offset) {
_range_op(*this, src, len, seek_offset, Write_func()); }
/**
* Allocate needed chunks
*/
void alloc(size_t len, offset_t seek_offset) {
_range_op(*this, (char*)0, len, seek_offset, Alloc_func()); }
/**
* Read data from chunk
*/
void read(char *dst, size_t len, offset_t seek_offset) const {
_range_op(*this, dst, len, seek_offset, Read_func()); }
/**
* Check for chunk availability
*/
void stat(size_t len, offset_t seek_offset) const {
_range_op(*this, (char*)0, len, seek_offset, Stat_func()); }
/**
* Synchronize chunk when dirty
*/
void sync(size_t len, offset_t seek_offset) const {
_range_op(*this, (char*)0, len, seek_offset, Sync_func()); }
/**
* Free chunks
*/
void free(size_t len, offset_t seek_offset)
{
unsigned const index = _index_by_offset(seek_offset);
Genode::destroy(&_alloc, _entries[index]);
_entries[index] = 0;
for (unsigned i = 0; i < NUM_ENTRIES; i++)
if (_entries[i])
return;
if (_parent) _parent->free(SIZE, _base_offset);
}
/**
* Truncate chunk to specified size in bytes
*
* This function can be used to shrink a chunk only. Specifying a
* 'size' larger than 'used_size' has no effect. The value returned
* by 'used_size' refers always to the position of the last byte
* written to the chunk.
*/
void truncate(size_t size)
{
unsigned const trunc_index = _index_by_offset(size);
if (trunc_index >= _num_entries)
return;
for (unsigned i = trunc_index + 1; i < _num_entries; i++)
_destroy_entry(i);
/* traverse into sub chunks */
if (_entries[trunc_index])
_entries[trunc_index]->truncate(size);
_num_entries = trunc_index + 1;
/*
* If the truncated at a chunk boundary, we can release the
* empty trailing chunk at 'trunc_index'.
*/
if (_entries[trunc_index] && _entries[trunc_index]->empty()) {
_destroy_entry(trunc_index);
_num_entries--;
}
}
};
};
#endif /* _CHUNK_H_ */

View File

@ -0,0 +1,433 @@
/*
* \brief Cache driver
* \author Stefan Kalkowski
* \date 2013-12-05
*/
/*
* Copyright (C) 2013 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 <base/printf.h>
#include <block_session/connection.h>
#include <block/component.h>
#include <os/packet_allocator.h>
#include "chunk.h"
/**
* Cache driver used by the generic block driver framework
*
* \param POLICY the cache replacement policy (e.g. LRU)
*/
template <typename POLICY>
class Driver : public Block::Driver
{
private:
/**
* This class encapsulates requests to the backend device in progress,
* and the packets from the client side that triggered the request.
*/
struct Request : public Genode::List<Request>::Element
{
Block::Packet_descriptor srv;
Block::Packet_descriptor cli;
char * const buffer;
Request(Block::Packet_descriptor &s,
Block::Packet_descriptor &c,
char * const b)
: srv(s), cli(c), buffer(b) {}
/*
* \return true when the given response packet matches
* the request send to the backend device
*/
bool match(const Block::Packet_descriptor& reply) const
{
return reply.operation() == srv.operation() &&
reply.block_number() == srv.block_number() &&
reply.block_count() == srv.block_count();
}
/*
* \param write whether it's a write or read request
* \param nr block number requested
* \param cnt number of blocks requested
* \return true when the given parameters match
* the request send to the backend device
*/
bool match(const bool write,
const Block::sector_t nr,
const Genode::size_t cnt) const
{
Block::Packet_descriptor::Opcode op = write
? Block::Packet_descriptor::WRITE
: Block::Packet_descriptor::READ;
return op == srv.operation() &&
nr >= srv.block_number() &&
nr+cnt <= srv.block_number()+srv.block_count();
}
};
/**
* Write failed exception at a specific device offset,
* can be triggered whenever the backend device is not ready
* to proceed
*/
struct Write_failed : Genode::Exception
{
Cache::offset_t off;
Write_failed(Cache::offset_t o) : off(o) {}
};
/*
* The given policy class is extended by a synchronization routine,
* used by the cache chunk structure
*/
struct Policy : POLICY {
static void sync(const typename POLICY::Element *e, char *src); };
public:
enum {
SLAB_SZ = Block::Session::TX_QUEUE_SIZE*sizeof(Request),
CACHE_BLK_SIZE = 4096
};
/**
* We use five levels of page-table like chunk structure,
* thereby we've a maximum device size of 256^4*4096 (LBA48)
*/
typedef Cache::Chunk<CACHE_BLK_SIZE, Policy> Chunk_level_4;
typedef Cache::Chunk_index<256, Chunk_level_4, Policy> Chunk_level_3;
typedef Cache::Chunk_index<256, Chunk_level_3, Policy> Chunk_level_2;
typedef Cache::Chunk_index<256, Chunk_level_2, Policy> Chunk_level_1;
typedef Cache::Chunk_index<256, Chunk_level_1, Policy> Chunk_level_0;
private:
static Driver *_instance; /* singleton instance */
Genode::Tslab<Request, SLAB_SZ> _r_slab; /* slab for requests */
Genode::List<Request> _r_list; /* list of requests */
Genode::Packet_allocator _alloc; /* packet allocator */
Block::Connection _blk; /* backend device */
Block::Session::Operations _ops; /* allowed operations */
Genode::size_t _blk_sz; /* block size */
Block::sector_t _blk_cnt; /* block count */
Chunk_level_0 _cache; /* chunk hierarchy */
Genode::Signal_rpc_member<Driver> _source_ack;
Genode::Signal_rpc_member<Driver> _source_submit;
Genode::Signal_rpc_member<Driver> _yield;
Driver(Driver const&); /* singleton pattern */
Driver& operator=(Driver const&); /* singleton pattern */
/*
* Return modulus of cache's versus backend device's block size
*/
inline int _cache_blk_mod() { return CACHE_BLK_SIZE / _blk_sz; }
/*
* Round off given block number to cache block size granularity
*/
inline Block::sector_t _cache_blk_round_off(Block::sector_t nr) {
return nr - (nr % _cache_blk_mod()); }
/*
* Round up given block number to cache block size granularity
*/
inline Block::sector_t _cache_blk_round_up(Block::sector_t nr) {
return (nr % _cache_blk_mod())
? nr + _cache_blk_mod() - (nr % _cache_blk_mod())
: nr; }
/*
* Handle response to a single request
*
* \param srv packet received from the backend device
* \param r outstanding request
*/
inline void _handle_reply(Block::Packet_descriptor &srv, Request *r)
{
try {
if (r->cli.operation() == Block::Packet_descriptor::READ)
read(r->cli.block_number(), r->cli.block_count(),
r->buffer, r->cli);
else
write(r->cli.block_number(), r->cli.block_count(),
r->buffer, r->cli);
} catch(Block::Driver::Request_congestion) {
PWRN("cli (%lld %zu) srv (%lld %zu)",
r->cli.block_number(), r->cli.block_count(),
r->srv.block_number(), r->srv.block_count());
}
}
/*
* Handle acknowledgements from the backend device
*/
void _ack_avail(unsigned)
{
while (_blk.tx()->ack_avail()) {
Block::Packet_descriptor p = _blk.tx()->get_acked_packet();
/* when reading, write result into cache */
if (p.operation() == Block::Packet_descriptor::READ)
_cache.write(_blk.tx()->packet_content(p),
p.block_count() * _blk_sz,
p.block_number() * _blk_sz);
/* loop through the list of requests, and ack all related */
for (Request *r = _r_list.first(), *r_to_handle = r; r;
r_to_handle = r) {
r = r->next();
if (r_to_handle->match(p)) {
_handle_reply(p, r_to_handle);
_r_list.remove(r_to_handle);
Genode::destroy(&_r_slab, r_to_handle);
}
}
_blk.tx()->release_packet(p);
}
}
/*
* Handle that the backend device is ready to receive again
*/
void _ready_to_submit(unsigned) { }
/*
* Setup a request to the backend device
*
* \param block_number block number offset
* \param block_count number of blocks
* \param packet original packet request received from the client
*/
void _request(Block::sector_t block_number,
Genode::size_t block_count,
char * const buffer,
Block::Packet_descriptor &packet)
{
Block::Packet_descriptor p_to_dev;
try {
/* we've to look whether the request is already pending */
for (Request *r = _r_list.first(); r; r = r->next()) {
if (r->match(false, block_number, block_count)) {
_r_list.insert(new (&_r_slab) Request(r->srv, packet,
buffer));
return;
}
}
/* it doesn't pay, we've to send a request to the device */
if (!_blk.tx()->ready_to_submit()) {
PWRN("not ready_to_submit");
throw Request_congestion();
}
/* read ahead CACHE_BLK_SIZE */
Block::sector_t nr = _cache_blk_round_off(block_number);
Genode::size_t cnt = _cache_blk_round_up(block_count +
(block_number - nr));
/* ensure all memory is available before sending the request */
_cache.alloc(cnt * _blk_sz, nr * _blk_sz);
/* construct and send the packet */
p_to_dev =
Block::Packet_descriptor(_blk.dma_alloc_packet(_blk_sz*cnt),
Block::Packet_descriptor::READ,
nr, cnt);
_r_list.insert(new (&_r_slab) Request(p_to_dev, packet, buffer));
_blk.tx()->submit_packet(p_to_dev);
} catch(Block::Session::Tx::Source::Packet_alloc_failed) {
throw Request_congestion();
} catch(Genode::Allocator::Out_of_memory) {
if (p_to_dev.valid()) /* clean up */
_blk.tx()->release_packet(p_to_dev);
throw Request_congestion();
}
}
/*
* Synchronize dirty chunks with backend device
*/
void _sync()
{
Cache::offset_t off = 0;
Cache::size_t len = _blk_sz * _blk_cnt;
while (len > 0) {
try {
_cache.sync(len, off);
len = 0;
} catch(Write_failed &e) {
/**
* Write to backend failed when backend device isn't ready
* to proceed, so handle signals, until it's ready again
*/
off = e.off;
len = _blk_sz * _blk_cnt - off;
Server::wait_and_dispatch_one_signal();
}
}
}
/*
* Check for chunk availability
*
* \param nr block number offset
* \param cnt number of blocks
* \param p client side packet, which triggered this operation
*/
bool _stat(Block::sector_t nr, Genode::size_t cnt,
char * const buffer, Block::Packet_descriptor &p)
{
Cache::offset_t off = nr * _blk_sz;
Cache::size_t size = cnt * _blk_sz;
Cache::offset_t end = off + size;
try {
_cache.stat(size, off);
return true;
} catch(Cache::Chunk_base::Range_incomplete &e) {
off = Genode::max(off, e.off);
size = Genode::min(end - off, e.size);
_request(off / _blk_sz, size / _blk_sz, buffer, p);
}
return false;
}
/*
* Signal handler for yield requests of the parent
*/
void _parent_yield(unsigned)
{
using namespace Genode;
Parent::Resource_args const args = env()->parent()->yield_request();
size_t const requested_ram_quota =
Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0);
/* flush the requested amount of RAM from cache */
POLICY::flush(requested_ram_quota);
env()->parent()->yield_response();
}
/*
* Constructor
*
* \param ep server entrypoint
*/
Driver(Server::Entrypoint &ep)
: _r_slab(Genode::env()->heap()),
_alloc(Genode::env()->heap(), CACHE_BLK_SIZE),
_blk(&_alloc, Block::Session::TX_QUEUE_SIZE*CACHE_BLK_SIZE),
_blk_sz(0),
_blk_cnt(0),
_cache(*Genode::env()->heap(), 0),
_source_ack(ep, *this, &Driver::_ack_avail),
_source_submit(ep, *this, &Driver::_ready_to_submit),
_yield(ep, *this, &Driver::_parent_yield)
{
_blk.info(&_blk_cnt, &_blk_sz, &_ops);
_blk.tx_channel()->sigh_ack_avail(_source_ack);
_blk.tx_channel()->sigh_ready_to_submit(_source_submit);
Genode::env()->parent()->yield_sigh(_yield);
if (CACHE_BLK_SIZE % _blk_sz) {
PERR("only devices that block size is divider of %x supported",
CACHE_BLK_SIZE);
throw Io_error();
}
/* truncate chunk structure to real size of the device */
_cache.truncate(_blk_sz*_blk_cnt);
}
public:
~Driver()
{
/* when session gets closed, synchronize and flush the cache */
_sync();
POLICY::flush();
}
static Driver* instance(Server::Entrypoint &ep) {
_instance = new (Genode::env()->heap()) Driver(ep);
return _instance;
}
static Driver* instance() { return _instance; }
static void destroy()
{
Genode::destroy(Genode::env()->heap(), _instance);
_instance = 0;
}
Block::Session_client* blk() { return &_blk; }
Genode::size_t blk_sz() { return _blk_sz; }
/****************************
** Block-driver interface **
****************************/
Genode::size_t block_size() { return _blk_sz; }
Block::sector_t block_count() { return _blk_cnt; }
Block::Session::Operations ops() { return _ops; }
void read(Block::sector_t block_number,
Genode::size_t block_count,
char* buffer,
Block::Packet_descriptor &packet)
{
if (!_ops.supported(Block::Packet_descriptor::READ))
throw Io_error();
if (!_stat(block_number, block_count, buffer, packet))
return;
_cache.read(buffer, block_count*_blk_sz, block_number*_blk_sz);
ack_packet(packet);
}
void write(Block::sector_t block_number,
Genode::size_t block_count,
const char * buffer,
Block::Packet_descriptor &packet)
{
if (!_ops.supported(Block::Packet_descriptor::WRITE))
throw Io_error();
_cache.alloc(block_count * _blk_sz, block_number * _blk_sz);
if ((block_number % _cache_blk_mod()) &&
_stat(block_number, 1, const_cast<char* const>(buffer), packet))
return;
if (((block_number+block_count) % _cache_blk_mod())
&& _stat(block_number+block_count-1, 1,
const_cast<char* const>(buffer), packet))
return;
_cache.write(buffer, block_count * _blk_sz,
block_number * _blk_sz);
ack_packet(packet);
}
void sync() { _sync(); }
};

View File

@ -0,0 +1,62 @@
/*
* \brief Least-recently-used cache replacement strategy
* \author Stefan Kalkowski
* \date 2013-12-05
*/
/*
* Copyright (C) 2013 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 <base/printf.h>
#include "lru.h"
#include "driver.h"
typedef Driver<Lru_policy>::Chunk_level_4 Chunk;
static const Lru_policy::Element *lru = 0;
static Genode::List<Lru_policy::Element> lru_list;
static void lru_access(const Lru_policy::Element *e)
{
if (e == lru) return;
if (e->next()) lru_list.remove(e);
lru_list.insert(e, lru);
lru = e;
}
void Lru_policy::read(const Lru_policy::Element *e) {
lru_access(e); }
void Lru_policy::write(const Lru_policy::Element *e) {
lru_access(e); }
void Lru_policy::flush(Cache::size_t size)
{
Cache::size_t s = 0;
for (Lru_policy::Element *e = lru_list.first();
e && ((size == 0) || (s < size));
e = lru_list.first(), s += sizeof(Chunk)) {
Chunk *cb = static_cast<Chunk*>(e);
e = e->next();
try {
cb->free(Driver<Lru_policy>::CACHE_BLK_SIZE,
cb->base_offset());
lru_list.remove(cb);
} catch(Chunk::Dirty_chunk &e) {
cb->sync(e.size, e.off);
}
}
if (!lru_list.first()) lru = 0;
if (s < size) throw Block::Driver::Request_congestion();
}

View File

@ -0,0 +1,25 @@
/*
* \brief Least-recently-used cache replacement strategy
* \author Stefan Kalkowski
* \date 2013-12-05
*/
/*
* Copyright (C) 2013 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 <util/list.h>
#include "chunk.h"
struct Lru_policy
{
class Element : public Genode::List<Element>::Element {};
static void read(const Element *e);
static void write(const Element *e);
static void flush(Cache::size_t size = 0);
};

View File

@ -0,0 +1,84 @@
/*
* \brief Cache a block device
* \author Stefan Kalkowski
* \date 2013-12-05
*/
/*
* Copyright (C) 2013 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 <os/server.h>
#include "lru.h"
#include "driver.h"
template <typename POLICY> Driver<POLICY>* Driver<POLICY>::_instance = 0;
/**
* Synchronize a chunk with the backend device
*/
template <typename POLICY>
void Driver<POLICY>::Policy::sync(const typename POLICY::Element *e, char *dst)
{
Cache::offset_t off =
static_cast<const Driver<POLICY>::Chunk_level_4*>(e)->base_offset();
if (!Driver::instance()->blk()->tx()->ready_to_submit())
throw Write_failed(off);
try {
Block::Packet_descriptor
p(Driver::instance()->blk()->dma_alloc_packet(Driver::CACHE_BLK_SIZE),
Block::Packet_descriptor::WRITE,
off / Driver::instance()->blk_sz(),
Driver::CACHE_BLK_SIZE / Driver::instance()->blk_sz());
Driver::instance()->blk()->tx()->submit_packet(p);
} catch(Block::Session::Tx::Source::Packet_alloc_failed) {
throw Write_failed(off);
}
}
struct Main
{
Server::Entrypoint &ep;
struct Factory : Block::Driver_factory
{
Server::Entrypoint &ep;
Factory(Server::Entrypoint &ep) : ep(ep) {}
Block::Driver *create() { return Driver<Lru_policy>::instance(ep); }
void destroy(Block::Driver *driver) { Driver<Lru_policy>::destroy(); }
} factory;
void resource_handler(unsigned) { }
Block::Root root;
Server::Signal_rpc_member<Main> resource_dispatcher;
Main(Server::Entrypoint &ep)
: ep(ep), factory(ep), root(ep, Genode::env()->heap(), factory),
resource_dispatcher(ep, *this, &Main::resource_handler)
{
Genode::env()->parent()->announce(ep.manage(root));
Genode::env()->parent()->resource_avail_sigh(resource_dispatcher);
}
};
/************
** Server **
************/
namespace Server {
char const *name() { return "blk_cache_ep"; }
size_t stack_size() { return 2*1024*sizeof(long); }
void construct(Entrypoint &ep) { static Main server(ep); }
}

View File

@ -0,0 +1,3 @@
TARGET = blk_cache
LIBS = base server
SRC_CC = main.cc lru.cc

View File

@ -0,0 +1,19 @@
ROM service that provides ROM modules that change during the lifetime of a ROM
session according to a timeline. This main purpose of this service is the
testing of programs that are able to respond to ROM module changes, for example
configuration changes.
The configuration of the dynamic ROM server contains a '<rom>' sub node per
ROM module provided by the service. Each '<rom>' node hosts a 'name' attribute
and contains a sequence of sub nodes that define the timeline of the ROM
module. The possible sub nodes are:
':<inline>:' The content of the '<inline>' node is assigned to the content
of the ROM module.
':<sleep>:' Sleeps a number of milliseconds as specified via the 'milliseconds'
attribute.
:'<empty>:' Removes the ROM module.
At the end of the timeline, the timeline re-starts at the beginning.

View File

@ -0,0 +1,288 @@
/*
* \brief ROM server that changes ROM content driven by time
* \author Norman Feske
* \date 2014-01-10
*/
/*
* Copyright (C) 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.
*/
/* Genode includes */
#include <util/volatile_object.h>
#include <util/arg_string.h>
#include <base/heap.h>
#include <base/env.h>
#include <root/component.h>
#include <os/server.h>
#include <os/config.h>
#include <os/attached_ram_dataspace.h>
#include <os/server.h>
#include <rom_session/rom_session.h>
#include <timer_session/connection.h>
namespace Dynamic_rom {
using Server::Entrypoint;
using Genode::Rpc_object;
using Genode::Sliced_heap;
using Genode::env;
using Genode::Lazy_volatile_object;
using Genode::Signal_context_capability;
using Genode::Xml_node;
using Genode::Signal_rpc_member;
using Genode::Arg_string;
class Session_component;
class Root;
struct Main;
}
class Dynamic_rom::Session_component : public Rpc_object<Genode::Rom_session>
{
private:
bool &_verbose;
Xml_node _rom_node;
Timer::Connection _timer;
unsigned _curr_idx = 0;
bool _has_content = false;
unsigned _last_content_idx = ~0;
Signal_context_capability _sigh;
Lazy_volatile_object<Genode::Attached_ram_dataspace> _ram_ds;
void _notify_client()
{
if (!_sigh.valid())
return;
Genode::Signal_transmitter(_sigh).submit();
}
template <typename... ARGS>
void _log(char const *format, ARGS... args)
{
if (!_verbose)
return;
char name[200];
_rom_node.attribute("name").value(name, sizeof(name));
Genode::printf("%s: ", name);
Genode::printf(format, args...);
Genode::printf("\n");
}
enum Execution_state { EXEC_CONTINUE, EXEC_BLOCK };
Execution_state _execute_step(Xml_node curr_step)
{
/*
* Replace content of ROM module by new one. Note that the content
* of the currently handed out dataspace remains untouched until
* the ROM client requests the new version by calling 'dataspace'
* the next time.
*/
if (curr_step.has_type("inline")) {
_last_content_idx = _curr_idx;
_has_content = true;
_notify_client();
if (curr_step.has_attribute("description")) {
char desc[200];
desc[0] = 0;
curr_step.attribute("description").value(desc, sizeof(desc));
_log("change (%s)", desc);
} else {
_log("change");
}
}
/*
* Remove ROM module
*/
if (curr_step.has_type("empty")) {
_last_content_idx = ~0;;
_has_content = false;
_notify_client();
_log("remove");
}
/*
* Sleep some time
*
* The timer will trigger the execution of the next step.
*/
if (curr_step.has_type("sleep")
&& curr_step.has_attribute("milliseconds")) {
unsigned long milliseconds = 0;
curr_step.attribute("milliseconds").value(&milliseconds);
_timer.trigger_once(milliseconds*1000);
_log("sleep %ld milliseconds", milliseconds);
return EXEC_BLOCK;
}
return EXEC_CONTINUE;
}
void _execute_steps_until_sleep()
{
Execution_state exec_state = EXEC_CONTINUE;
while (exec_state == EXEC_CONTINUE) {
exec_state = _execute_step(_rom_node.sub_node(_curr_idx));
/* advance step index, wrap at the end */
_curr_idx = (_curr_idx + 1) % _rom_node.num_sub_nodes();
}
}
void _timer_handler(unsigned) { _execute_steps_until_sleep(); }
Entrypoint &_ep;
typedef Session_component This;
Signal_rpc_member<This> _timer_dispatcher = { _ep, *this, &This::_timer_handler };
public:
Session_component(Entrypoint &ep, Xml_node rom_node, bool &verbose)
:
_verbose(verbose), _rom_node(rom_node), _ep(ep)
{
/* init timer signal handler */
_timer.sigh(_timer_dispatcher);
/* execute first step immediately at session-creation time */
_execute_steps_until_sleep();
}
~Session_component()
{
_ep.dissolve(_timer_dispatcher);
}
Genode::Rom_dataspace_capability dataspace() override
{
using namespace Genode;
if (!_has_content)
return Rom_dataspace_capability();
/* replace dataspace by new one */
_ram_ds.construct(env()->ram_session(), _rom_node.size());
/* fill with content of current step */
Xml_node step_node = _rom_node.sub_node(_last_content_idx);
memcpy(_ram_ds->local_addr<char>(),
step_node.content_addr(),
step_node.content_size());
/* cast RAM into ROM dataspace capability */
Dataspace_capability ds_cap = static_cap_cast<Dataspace>(_ram_ds->cap());
return static_cap_cast<Rom_dataspace>(ds_cap);
}
void sigh(Genode::Signal_context_capability sigh) override
{
_sigh = sigh;
}
};
class Dynamic_rom::Root : public Genode::Root_component<Session_component>
{
private:
Entrypoint &_ep;
Xml_node _config_node;
bool &_verbose;
class Nonexistent_rom_module { };
Xml_node _lookup_rom_node_in_config(char const *name)
{
/* lookup ROM module in config */
for (unsigned i = 0; i < _config_node.num_sub_nodes(); i++) {
Xml_node node = _config_node.sub_node(i);
if (node.has_attribute("name")
&& node.attribute("name").has_value(name))
return node;
}
throw Nonexistent_rom_module();
}
protected:
Session_component *_create_session(const char *args)
{
/* read name of ROM module from args */
char name[200];
Arg_string::find_arg(args, "filename").string(name, sizeof(name), "");
try {
return new (md_alloc())
Session_component(_ep,
_lookup_rom_node_in_config(name),
_verbose);
} catch (Nonexistent_rom_module) {
PERR("ROM module lookup for \"%s\" failed.", name);
throw Root::Invalid_args();
}
}
public:
Root(Entrypoint &ep, Genode::Allocator &md_alloc,
Xml_node config_node, bool &verbose)
:
Genode::Root_component<Session_component>(&ep.rpc_ep(), &md_alloc),
_ep(ep), _config_node(config_node), _verbose(verbose)
{ }
};
struct Dynamic_rom::Main
{
bool _verbose_config()
{
Xml_node config = Genode::config()->xml_node();
return config.has_attribute("verbose")
& config.attribute("verbose").has_value("yes");
}
bool verbose = _verbose_config();
Entrypoint &ep;
Sliced_heap sliced_heap = { env()->ram_session(), env()->rm_session() };
Root root = { ep, sliced_heap, Genode::config()->xml_node(), verbose };
Main(Entrypoint &ep) : ep(ep)
{
env()->parent()->announce(ep.manage(root));
}
};
namespace Server {
char const *name() { return "dynamic_rom_ep"; }
size_t stack_size() { return 4*1024*sizeof(long); }
void construct(Entrypoint &ep)
{
static Dynamic_rom::Main main(ep);
}
}

View File

@ -0,0 +1,3 @@
TARGET = dynamic_rom
SRC_CC = main.cc
LIBS = base server config

View File

@ -0,0 +1,16 @@
The 'fs_rom' server translates the 'File_system' session interface to the
'ROM' session' interface. Each request for a ROM file is handled by looking
up an equally named file on the file system. If no such file could be found,
the server watches the file system for the creation of the corresponding file.
Furthermore, the server reflects file changes as signals to the ROM session.
Limitations
-----------
* Symbolic links are not handled
* The server needs to allocate RAM for each requested file. The RAM is always
allocated from the RAM session of the server. The RAM quota consumed by the
server depends on the client requests and the size of the requested files.
Therefore, one instance of the server should not be used by untrusted clients
and critical clients at the same time. In such situations, multiple instances
of the server could be used.

View File

@ -0,0 +1,470 @@
/*
* \brief Service that provides files of a file system as ROM sessions
* \author Norman Feske
* \date 2013-01-11
*/
/*
* Copyright (C) 2013 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 <rom_session/rom_session.h>
#include <root/component.h>
#include <cap_session/connection.h>
#include <file_system_session/connection.h>
#include <util/arg_string.h>
#include <base/rpc_server.h>
#include <base/env.h>
#include <base/printf.h>
#include <os/path.h>
/*********************************************
** Utilities for accessing the file system **
*********************************************/
/*
* XXX The following generic utilities should be moved to a public place.
* They are based on those found in the 'libc_fs' plugin. We should
* unify them.
*/
namespace File_system {
/**
* Collect pending packet acknowledgements, freeing the space occupied
* by the packet in the bulk buffer
*
* This function should be called prior enqueing new packets into the
* packet stream to free up space in the bulk buffer.
*/
static void collect_acknowledgements(Session::Tx::Source &source)
{
while (source.ack_avail())
source.release_packet(source.get_acked_packet());
}
/**
* Read file content
*/
static inline size_t read(Session &fs, File_handle const &file_handle,
void *dst, size_t count, off_t seek_offset = 0)
{
Session::Tx::Source &source = *fs.tx();
size_t const max_packet_size = source.bulk_buffer_size() / 2;
size_t remaining_count = count;
while (remaining_count) {
collect_acknowledgements(source);
size_t const curr_packet_size = min(remaining_count, max_packet_size);
Packet_descriptor
packet(source.alloc_packet(curr_packet_size),
0,
file_handle,
File_system::Packet_descriptor::READ,
curr_packet_size,
seek_offset);
/* pass packet to server side */
source.submit_packet(packet);
packet = source.get_acked_packet();
size_t const read_num_bytes = min(packet.length(), curr_packet_size);
/* copy-out payload into destination buffer */
memcpy(dst, source.packet_content(packet), read_num_bytes);
source.release_packet(packet);
/* prepare next iteration */
seek_offset += read_num_bytes;
dst = (void *)((Genode::addr_t)dst + read_num_bytes);
remaining_count -= read_num_bytes;
/*
* If we received less bytes than requested, we reached the end
* of the file.
*/
if (read_num_bytes < curr_packet_size)
break;
}
return count - remaining_count;
}
struct Handle_guard
{
private:
Session &_session;
Node_handle _handle;
public:
Handle_guard(Session &session, Node_handle handle)
: _session(session), _handle(handle) { }
~Handle_guard() { _session.close(_handle); }
};
}
/*****************
** ROM service **
*****************/
/**
* A 'Rom_session_component' exports a single file of the file system
*/
class Rom_session_component : public Genode::Rpc_object<Genode::Rom_session>
{
private:
File_system::Session &_fs;
enum { PATH_MAX_LEN = 512 };
typedef Genode::Path<PATH_MAX_LEN> Path;
/**
* Name of requested file, interpreted at path into the file system
*/
Path const _file_path;
/**
* Handle of associated file
*/
File_system::File_handle _file_handle;
/**
* Handle of currently watched compound directory
*
* The compund directory is watched only if the requested file could
* not be looked up.
*/
File_system::Node_handle _compound_dir_handle;
/**
* Dataspace exposed as ROM module to the client
*/
Genode::Ram_dataspace_capability _file_ds;
/**
* Handler for ROM file changes
*/
Genode::Lock _sigh_lock;
Genode::Signal_context_capability _sigh;
/**
* Dispatcher that is called each time when the requested file is not
* yet available and the compound directory changes
*
* The change of the compound directory bears the chance that the
* requested file re-appears. So we inform the client about a ROM
* module change and thereby give it a chance to call 'dataspace()' in
* response.
*/
Genode::Signal_dispatcher<Rom_session_component> _dir_change_dispatcher;
/**
* Signal-handling function called by the main thread the compound
* directory changed.
*
* Note that this function is not executed in the context of the RPC
* entrypoint. Therefore, the access to '_sigh' is synchronized with
* the 'sigh()' function using '_sigh_lock'.
*/
void _dir_changed(unsigned)
{
Genode::Lock::Guard guard(_sigh_lock);
PINF("detected directory change");
if (_sigh.valid())
Genode::Signal_transmitter(_sigh).submit();
}
/**
* Open compound directory of specified file
*
* \param walk_up If set to true, the function tries to walk up the
* hierarchy towards the root and returns the first
* existing directory on the way. If set to false, the
* function returns the immediate compound directory.
*/
static File_system::Dir_handle _open_compound_dir(File_system::Session &fs,
Path const &path,
bool walk_up)
{
using namespace File_system;
Genode::Path<PATH_MAX_LEN> dir_path(path.base());
while (!path.equals("/")) {
dir_path.strip_last_element();
try { return fs.dir(dir_path.base(), false); }
catch (Invalid_handle) { PERR("Invalid_handle"); }
catch (Invalid_name) { PERR("Invalid_name"); }
catch (Lookup_failed) { PERR("Lookup_failed"); }
catch (Permission_denied) { PERR("Permission_denied"); }
catch (Name_too_long) { PERR("Name_too_long"); }
catch (No_space) { PERR("No_space"); }
/*
* If the directory could not be opened, walk up the hierarchy
* towards the root and try again.
*/
if (!walk_up) break;
}
return Dir_handle(); /* invalid */
}
/**
* Open file with specified name at the file system
*/
static File_system::File_handle _open_file(File_system::Session &fs,
Path const &path)
{
using namespace File_system;
File_system::File_handle file_handle;
try {
Dir_handle dir = _open_compound_dir(fs, path, false);
Handle_guard guard(fs, dir);
/* open file */
Genode::Path<PATH_MAX_LEN> file_name(path.base());
file_name.keep_only_last_element();
file_handle = fs.file(dir, file_name.base() + 1,
File_system::READ_ONLY, false);
}
catch (Invalid_handle) { PERR("Invalid_handle"); }
catch (Invalid_name) { PERR("Invalid_name"); }
catch (Lookup_failed) { PERR("Lookup_failed"); }
return file_handle;
}
void _register_for_compound_dir_changes()
{
/* forget about the previously watched compound directory */
if (_compound_dir_handle.valid())
_fs.close(_compound_dir_handle);
_compound_dir_handle = _open_compound_dir(_fs, _file_path, true);
/* register for changes in compound directory */
if (_compound_dir_handle.valid())
_fs.sigh(_compound_dir_handle, _dir_change_dispatcher);
else
PWRN("could not track compound dir, giving up");
}
/**
* Initialize '_file_ds' dataspace with file content
*/
void _update_dataspace()
{
using namespace File_system;
/*
* On each repeated call of this function, the dataspace is
* replaced with a new one that contains the most current file
* content.
*/
if (_file_ds.valid()) {
env()->ram_session()->free(_file_ds);
_file_ds = Ram_dataspace_capability();
}
/* close and then re-open the file */
if (_file_handle.valid())
_fs.close(_file_handle);
_file_handle = _open_file(_fs, _file_path);
/*
* If we got the file, we can stop paying attention to the
* compound directory.
*/
if (_file_handle.valid() && _compound_dir_handle.valid())
_fs.close(_compound_dir_handle);
/* register for file changes */
if (_sigh.valid() && _file_handle.valid())
_fs.sigh(_file_handle, _sigh);
size_t const file_size = _file_handle.valid()
? _fs.status(_file_handle).size : 0;
/* allocate new RAM dataspace according to file size */
if (file_size > 0) {
try {
_file_ds = env()->ram_session()->alloc(file_size); }
catch (...) {
PERR("couldn't allocate memory for file, empty result\n");
_file_ds = Ram_dataspace_capability();
return;
}
}
if (!_file_ds.valid()) {
_register_for_compound_dir_changes();
return;
}
/* map dataspace locally */
void * const dst_addr = env()->rm_session()->attach(_file_ds);
/* read content from file */
read(_fs, _file_handle, dst_addr, file_size);
/* unmap dataspace */
env()->rm_session()->detach(dst_addr);
}
public:
/**
* Constructor
*
* \param fs file-system session to read the file from
* \param filename requested file name
* \param sig_rec signal receiver used to get notified about changes
* within the compound directory (in the case when
* the requested file could not be found at session-
* creation time)
*/
Rom_session_component(File_system::Session &fs, const char *file_path,
Genode::Signal_receiver &sig_reg)
:
_fs(fs), _file_path(file_path), _file_handle(_open_file(_fs, _file_path)),
_dir_change_dispatcher(sig_reg, *this, &Rom_session_component::_dir_changed)
{
if (!_file_handle.valid())
_register_for_compound_dir_changes();
}
/**
* Destructor
*/
~Rom_session_component()
{
/* close re-open the file */
if (_file_handle.valid())
_fs.close(_file_handle);
if (_compound_dir_handle.valid())
_fs.close(_compound_dir_handle);
/* close file */
Genode::env()->ram_session()->free(_file_ds);
}
/**
* Return dataspace with up-to-date content of file
*/
Genode::Rom_dataspace_capability dataspace()
{
_update_dataspace();
Genode::Dataspace_capability ds = _file_ds;
return Genode::static_cap_cast<Genode::Rom_dataspace>(ds);
}
void sigh(Genode::Signal_context_capability sigh)
{
Genode::Lock::Guard guard(_sigh_lock);
_sigh = sigh;
if (_file_handle.valid())
_fs.sigh(_file_handle, _sigh);
}
};
class Rom_root : public Genode::Root_component<Rom_session_component>
{
private:
File_system::Session &_fs;
Genode::Signal_receiver &_sig_rec;
Rom_session_component *_create_session(const char *args)
{
enum { FILENAME_MAX_LEN = 128 };
char filename[FILENAME_MAX_LEN];
Genode::Arg_string::find_arg(args, "filename")
.string(filename, sizeof(filename), "");
PINF("connection for file '%s' requested\n", filename);
/* create new session for the requested file */
return new (md_alloc()) Rom_session_component(_fs, filename, _sig_rec);
}
public:
/**
* Constructor
*
* \param entrypoint entrypoint to be used for ROM sessions
* \param md_alloc meta-data allocator used for ROM sessions
* \param fs file-system session
*/
Rom_root(Genode::Rpc_entrypoint &entrypoint,
Genode::Allocator &md_alloc,
File_system::Session &fs,
Genode::Signal_receiver &sig_rec)
:
Genode::Root_component<Rom_session_component>(&entrypoint, &md_alloc),
_fs(fs), _sig_rec(sig_rec)
{ }
};
int main(void)
{
using namespace Genode;
/* open file-system session */
static Genode::Allocator_avl fs_tx_block_alloc(env()->heap());
static File_system::Connection fs(fs_tx_block_alloc);
/* connection to capability service needed to create capabilities */
static Cap_connection cap;
/* creation of the entrypoint and the root interface */
static Sliced_heap sliced_heap(env()->ram_session(),
env()->rm_session());
/* receiver of directory-change signals */
static Signal_receiver sig_rec;
enum { STACK_SIZE = 8*1024 };
static Rpc_entrypoint ep(&cap, STACK_SIZE, "fs_rom_ep");
static Rom_root rom_root(ep, sliced_heap, fs, sig_rec);
/* announce server*/
env()->parent()->announce(ep.manage(&rom_root));
/* process incoming signals */
for (;;) {
Signal s = sig_rec.wait_for_signal();
static_cast<Signal_dispatcher_base *>(s.context())->dispatch(s.num());
}
return 0;
}

View File

@ -0,0 +1,3 @@
TARGET = fs_rom
SRC_CC = main.cc
LIBS = base

View File

@ -0,0 +1,27 @@
This directory contains an implementation of an ISO 9660 file system.
Limitations
-----------
At the moment, the only file-name format supported is the Rock Ridge extension.
The ISO specified 8.3 upper-case-file names are not supported, as well as Joliet.
Usage
-----
The server requires an ATAPI-block device as back-end. Please have a look at
'os/src/drivers/atapi'. The front-end of the server is implemented as a ROM
session server. In order to access this server from your application, you need
to route the ROM session to the ISO-ROM-session server in Genode's configuration
file:
!<start name="test-iso">
! <resource name="RAM" quantum="10M" />
! <route>
! <service name="ROM"><child name="iso9660"/></service>
! </route>
!</start>
Currently, the RAM quota necessary to obtain a file from the ISO file system
is allocated on behalf of the ISO server. Please make sure to provide
sufficient RAM quota to the ISO server.

View File

@ -0,0 +1,257 @@
/*
* \brief Backing store for on-demand-paged managed dataspaces
* \author Norman Feske
* \date 2010-10-31
*/
/*
* Copyright (C) 2010-2013 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 _BACKING_STORE_H_
#define _BACKING_STORE_H_
#include <base/env.h>
/**
* Fifo-based physical backing-store allocator
*
* \param UMD user-specific metadata attached to each backing-store block,
* for example the corresponding offset within a managed
* dataspace
*/
template <typename UMD>
class Backing_store
{
public:
/**
* Interface to be implemented by the user of the backing store
*/
struct User
{
virtual void detach_block(UMD umd)
{
PWRN("this should never be called");
}
};
/**
* Pseudo user for marking a block as used during the
* time between the allocation and the assignment to the
* actual user
*/
User _not_yet_assigned;
/**
* Meta data of a backing-store block
*/
class Block
{
private:
friend class Backing_store;
/**
* User of the block, 0 if block is unused
*/
User *_user;
/**
* User-specific meta data. It is not used by the
* 'Backing_store', just handed back to the user with the
* 'detach_block' function.
*/
UMD _user_meta_data;
/**
* Default constructor used for array allocation
*/
Block() : _user(0) { }
/**
* Used by 'Backing_store::assign'
*/
void assign_user(User *user, UMD user_meta_data) {
_user = user, _user_meta_data = user_meta_data; }
/**
* Used by 'Backing_store::alloc'
*/
void assign_pseudo_user(User *user) { _user = user; }
/**
* Return true if block is in use
*/
bool is_occupied() const { return _user != 0; }
/**
* Return user of the block
*/
const User *user() const { return _user; }
/**
* Evict block, preparing to for reuse
*/
void evict()
{
if (_user)
_user->detach_block(_user_meta_data);
_user = 0;
}
};
private:
/**
* Lock for synchronizing the block allocator
*/
Genode::Lock _alloc_lock;
const Genode::size_t _block_size;
/**
* Number of physical blocks
*/
const Genode::size_t _num_blocks;
/**
* RAM dataspace holding the physical backing store
*/
const Genode::Dataspace_capability _ds;
/**
* Local address of physical backing store
*/
const void *_ds_addr;
/**
* Array of block meta data
*/
Block *_blocks;
/**
* Block index for next allocation (used for fifo)
*/
unsigned long _curr_block_idx;
/**
* Return meta data of next-to-be-allocated block
*/
Block *_curr_block() const { return &_blocks[_curr_block_idx]; }
/**
* Advance fifo allocation index
*/
void _advance_curr_block()
{
_curr_block_idx++;
if (_curr_block_idx == _num_blocks)
_curr_block_idx = 0;
}
/**
* Calculate number of blocks that fit into specified amount of RAM,
* taking the costs for meta data into account
*/
Genode::size_t _calc_num_blocks(Genode::size_t ram_size) const
{
return ram_size / (sizeof(Block) + _block_size);
}
public:
/**
* Constructor
*
* \param ram_size number of bytes of physical RAM to be used as
* backing store for both the meta data and the
* payload
*/
Backing_store(Genode::size_t ram_size, Genode::size_t block_size)
:
_block_size(block_size),
_num_blocks(_calc_num_blocks(ram_size)),
_ds(Genode::env()->ram_session()->alloc(_block_size*_num_blocks)),
_ds_addr(Genode::env()->rm_session()->attach(_ds)),
_blocks(new (Genode::env()->heap()) Block[_num_blocks]),
_curr_block_idx(0)
{ }
Block *alloc()
{
Genode::Lock::Guard guard(_alloc_lock);
/* skip blocks that are currently in the process of being assigned */
while (_curr_block()->user() == &_not_yet_assigned) {
PDBG("skipping not-yet assigned block");
_advance_curr_block();
}
/* evict block if needed */
if (_curr_block()->is_occupied())
_curr_block()->evict();
/* reserve allocated block (prevent eviction prior assignment) */
Block *block = _curr_block();
block->assign_pseudo_user(&_not_yet_assigned);
_advance_curr_block();
return block;
}
/**
* Return dataspace containing the backing store payload
*/
Genode::Dataspace_capability dataspace() const { return _ds; }
/**
* Return block size used by the backing store
*/
Genode::size_t block_size() const { return _block_size; }
/**
* Return block index of specified block
*/
Genode::off_t index(const Block *b) const { return b - _blocks; }
/**
* Return offset of block within physical backing store
*/
Genode::off_t offset(const Block *b) const {
return index(b)*_block_size; }
/**
* Return local address of physical backing-store block
*/
void *local_addr(const Block *b) const {
return (void *)((Genode::addr_t)_ds_addr + offset(b)); }
/**
* Assign final user of a block
*
* After calling this function, the block will be subjected to
* eviction, if needed.
*/
void assign(Block *block, User *user, UMD user_meta_data)
{
Genode::Lock::Guard guard(_alloc_lock);
block->assign_user(user, user_meta_data);
}
/**
* Evict all blocks currently in use by the specified user
*/
void flush(const User *user)
{
Genode::Lock::Guard guard(_alloc_lock);
for (unsigned i = 0; i < _num_blocks; i++)
if (_blocks[i].user() == user)
_blocks[i].evict();
}
};
#endif /* _BACKING_STORE_H_ */

View File

@ -0,0 +1,496 @@
/*
* \brief ISO 9660 file system support
* \author Sebastian Sumpf <sebastian.sumpf@genode-labs.com>
* \date 2010-07-26
*/
/*
* Copyright (C) 2010-2013 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/exception.h>
#include <base/printf.h>
#include <base/stdint.h>
#include <block_session/connection.h>
#include <util/misc_math.h>
#include <util/token.h>
#include "iso9660.h"
using namespace Genode;
namespace Iso {
/*
* Sector reads one or more packets from the block interface
*/
class Sector {
public:
enum {
MAX_SECTORS = 32, /* max. number sectors that can be read in one
transaction */
};
static Block::Connection *_blk;
static Block::Session::Tx::Source *_source;
static size_t _blk_size;
static Lock _lock;
private:
Block::Packet_descriptor _p;
public:
Sector(unsigned long blk_nr, unsigned long count)
{
Lock::Guard lock_guard(_lock);
try {
_p = Block::Packet_descriptor(
_blk->dma_alloc_packet(blk_size() * count),
Block::Packet_descriptor::READ,
blk_nr * ((float)blk_size() / _blk_size),
count * ((float)blk_size() / _blk_size));
_source->submit_packet(_p);
_p = _source->get_acked_packet();
if (!_p.succeeded()) {
PERR("Could not read block %lu", blk_nr);
throw Io_error();
}
} catch (Block::Session::Tx::Source::Packet_alloc_failed) {
PERR("Packet overrun!");
_p = _source->get_acked_packet();
throw Io_error();
}
}
~Sector() {
Lock::Guard lock_guard(_lock);
_source->release_packet(_p);
}
/**
* Return address of packet content
*/
template <typename T>
T addr() { return reinterpret_cast<T>(_source->packet_content(_p)); }
static size_t blk_size() { return 2048; }
static unsigned long to_blk(unsigned long bytes) {
return ((bytes + blk_size() - 1) & ~(blk_size() - 1)) / blk_size(); }
};
/**
* Rock ridge extension (see IEEE P1282)
*/
class Rock_ridge
{
public:
enum {
NM = 0x4d4e, /* POSIX name system use entry (little endian) */
};
private:
uint16_t _signature;
uint8_t _length;
uint8_t _version;
uint8_t _flags;
char _name;
public:
char *name() { return &_name; }
uint8_t length() { return _length - 5; }
static Rock_ridge* scan_name(uint8_t *ptr, uint8_t size)
{
Rock_ridge *rr = (Rock_ridge *)ptr;
while (rr->_length && ((uint8_t *)rr < ptr + size - 4)) {
if (rr->_signature == NM)
return rr;
rr = rr->next();
}
return 0;
}
Rock_ridge *next() { return (Rock_ridge *)((uint8_t *)this + _length); }
};
/*********************
** ISO9660 classes **
*********************/
/**
* Access memory using offsets
*/
class Iso_base
{
protected:
template <typename T>
T value(int offset) { return *((T *)(this + offset)); }
template <typename T>
T ptr(int offset) { return (T)(this + offset); }
};
/**
* Class representing a directory descriptions (see ECMA 119)
*/
class Directory_record : public Iso_base
{
enum {
TABLE_LENGTH = 33, /* fixed length of directory record */
ROOT_DIR = 0x0, /* special names of root and parent directories */
PARENT_DIR = 0x1,
DIR_FLAG = 0x2, /* directory flag in attributes */
};
public:
/* total length of this record */
uint8_t record_length() { return value<uint8_t>(0); }
/* starting block of extent */
uint32_t blk_nr() { return value<uint32_t>(2); }
/* length in bytes of extent */
uint32_t data_length() { return value<uint32_t>(10); }
/* attributes */
uint8_t file_flags() { return value<uint8_t>(25); }
/* length of file name */
uint8_t file_name_length() { return value<uint8_t>(32); }
/* retrieve the file name */
void file_name(char *buf)
{
buf[0] = 0;
/* try Rock Ridge name */
Rock_ridge *rr = Rock_ridge::scan_name(system_use(),
system_use_size());
if (rr) {
memcpy(buf, rr->name(), rr->length());
buf[rr->length()] = 0;
return;
}
/* retrieve iso name */
char *name = ptr<char*>(33);
/*
* Check for root and parent directory names and modify them
* to '.' and '..' respectively.
*/
if (file_name_length() == 1)
switch (name[0]) {
case PARENT_DIR:
buf[2] = 0;
buf[1] = '.';
buf[0] = '.';
return;
case ROOT_DIR:
buf[1] = 0;
buf[0] = '.';
return;
}
memcpy(buf, name, file_name_length());
buf[file_name_length()] = 0;
}
/* pad byte after file name (if file name length is even, only) */
uint8_t pad_byte() { return !(file_name_length() % 2) ? 1 : 0; }
/* system use area */
uint8_t *system_use()
{
return (uint8_t*)this + TABLE_LENGTH
+ file_name_length() + pad_byte();
}
/* length in bytes of system use area */
uint8_t system_use_size()
{
return record_length() - file_name_length()
- TABLE_LENGTH - pad_byte();
}
/* retrieve next record */
Directory_record *next()
{
Directory_record *_next = this + record_length();
if (_next->record_length())
return _next;
return 0;
}
/* find a directory record with file name matching 'level' */
Directory_record *locate(char *level)
{
Directory_record *dir = this;
while (dir) {
char name[LEVEL_LENGTH];
dir->file_name(name);
if (!strcmp(name, level))
return dir;
dir = dir->next();
}
return 0;
}
/* describes this record a directory */
bool is_directory() { return file_flags() & DIR_FLAG; }
};
/**
* Volume descriptor (see ECMA 119)
*/
class Volume_descriptor : public Iso_base
{
enum {
/* volume types */
PRIMARY = 0x01, /* type of primary volume descriptor */
TERMINATOR = 0xff, /* type of terminating descriptor */
/* constants */
ROOT_SIZE = 34, /* the root directory record has a fixed length */
};
public:
/* descriptor type */
uint8_t type() { return value<uint8_t>(0); }
/* root directory record */
Directory_record * root_record() { return ptr<Directory_record *>(156); }
/* check for primary descriptor */
bool is_primary() { return type() == PRIMARY; }
/* check for terminating descriptor */
bool is_terminator() { return type() == TERMINATOR; }
/* copy the root record */
Directory_record *copy_root_record()
{
Directory_record *buf;
if (!(env()->heap()->alloc(ROOT_SIZE, &buf)))
throw Root::Quota_exceeded();
memcpy(buf, root_record(), ROOT_SIZE);
return buf;
}
};
/**
* Locate the root-directory record in the primary volume descriptor
*/
Directory_record *locate_root()
{
/* volume descriptors in ISO9660 start at block 16 */
for (unsigned long blk_nr = 16;; blk_nr++) {
Sector sec(blk_nr, 1);
Volume_descriptor *vol = sec.addr<Volume_descriptor *>();
if (verbose)
PDBG("Volume: type %u", vol->type());
if (vol->is_primary())
return vol->copy_root_record();
if (vol->is_terminator())
return 0;
}
}
/**
* Return root directory record
*/
Directory_record *root_dir()
{
static Directory_record *root = locate_root();
if (!root)
throw Non_data_disc();
if (verbose)
PDBG("Good root");
return root;
}
unsigned long read_file(File_info *info, off_t file_offset, uint32_t length, void *buf_ptr)
{
uint8_t *buf = (uint8_t *)buf_ptr;
if (info->size() <= (size_t)(length + file_offset))
length = info->size() - file_offset - 1;
unsigned long total_blk_count = ((length + (Sector::blk_size() - 1)) &
~((Sector::blk_size()) - 1)) / Sector::blk_size();
unsigned long ret = total_blk_count;
unsigned long blk_count;
unsigned long blk_nr = info->blk_nr() + (file_offset / Sector::blk_size());
if (verbose)
PDBG("Read blk %lu count %lu, file_offset: %08lx length %u", blk_nr, total_blk_count, file_offset, length);
while ((blk_count = min<unsigned long>(Sector::MAX_SECTORS, total_blk_count))) {
Sector sec(blk_nr, blk_count);
total_blk_count -= blk_count;
blk_nr += blk_count;
unsigned long copy_length = blk_count * Sector::blk_size();
memcpy(buf, sec.addr<void *>(), copy_length);
length -= copy_length;
buf += copy_length;
/* zero out rest of page */
if (!total_blk_count && (blk_count % 2))
memset(buf, 0, Sector::blk_size());
}
return ret * Sector::blk_size();
}
struct Scanner_policy_file
{
static bool identifier_char(char c, unsigned /* i */)
{
return c != '/' && c != 0;
}
};
typedef ::Genode::Token<Scanner_policy_file> Token;
/**
* ISO interface
*/
File_info *file_info(char *path)
{
char level[PATH_LENGTH];
Token t(path);
Directory_record *dir = root_dir();
uint32_t blk_nr = 0, data_length = 0;
/* determine block nr and file length on disk, parse directory records */
while (t) {
if (t.type() != Token::IDENT) {
t = t.next();
continue;
}
t.string(level, PATH_LENGTH);
/* load extent of directory record and search for level */
for (unsigned long i = 0; i < Sector::to_blk(dir->data_length()); i++) {
Sector sec(dir->blk_nr() + i, 1);
Directory_record *tmp = sec.addr<Directory_record *>()->locate(level);
if (!tmp && i == Sector::to_blk(dir->data_length()) - 1) {
PERR("File not found: %s", path);
throw File_not_found();
}
if (!tmp) continue;
dir = tmp;
if (verbose)
PDBG("Found %s", level);
if (!dir->is_directory()) {
blk_nr = dir->blk_nr();
data_length = dir->data_length();
}
break;
}
t = t.next();
}
/* Warning: Don't access 'dir' after this point, since the sector is gone */
if (!blk_nr && !data_length) {
PERR("File not found: %s", path);
throw File_not_found();
}
if (verbose)
PDBG("Path: %s Block nr: %u Length: %u", path, blk_nr, data_length);
return new(env()->heap()) File_info(blk_nr, data_length);
}
/*
* Static members of Sector
*/
Block::Connection *Sector::_blk;
Block::Session::Tx::Source *Sector::_source;
size_t Sector::_blk_size;
Lock Sector::_lock;
/**
* Initialize globals
*/
void __attribute__((constructor)) init()
{
static Allocator_avl block_alloc(env()->heap());
static Block::Connection _blk(&block_alloc);
Sector::_blk = &_blk;
Sector::_source = _blk.tx();
Block::sector_t blk_cnt = 0;
Block::Session::Operations ops;
_blk.info(&blk_cnt, &Sector::_blk_size, &ops);
}
} /* end of namespace Iso */

View File

@ -0,0 +1,81 @@
/*
* \brief ISO interface to session server
* \author Sebastian Sumpf <Sebastian.Sumpf@genode-labs.com>
* \date 2010-07-26
*/
/*
* Copyright (C) 2010-2013 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 <rom_session/rom_session.h>
#include <base/stdint.h>
namespace Iso {
/*
* Exceptions
*/
class Io_error : public Genode::Exception { };
class Non_data_disc : public Genode::Exception { };
class File_not_found : public Genode::Exception { };
/* enable/disable debugging output */
const int verbose = 0;
enum {
PATH_LENGTH = 128, /* max. length of a path */
LEVEL_LENGTH = 32, /* max. length of a level of a path */
PAGE_SIZE = 4096,
};
class File_info
{
private:
Genode::uint32_t _blk_nr;
Genode::size_t _size;
public:
File_info(Genode::uint32_t blk_nr, Genode::size_t size)
: _blk_nr(blk_nr), _size(size) {}
Genode::uint32_t blk_nr() { return _blk_nr; }
Genode::size_t size() { return _size; }
Genode::size_t page_sized() { return (_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); }
};
/**
* Retrieve file information
*
* \param path absolute path of the file (slash separated)
*
* \throw File_not_found
* \throw Io_error
* \throw Non_data_disc
*
* \return Pointer to File_info class
*/
File_info *file_info(char *path);
/**
* Read data from ISO
*
* \param info File Info of file to read the data from
* \param file_offset Offset in file
* \param length Number of bytes to read
* \param buf Output buffer
*
* \throw Io_error
*
* \return Number of bytes read
*/
unsigned long read_file(File_info *info, Genode::off_t file_offset,
Genode::uint32_t length, void *buf);
}

View File

@ -0,0 +1,320 @@
/*
* \brief Rom-session server for ISO file systems
* \author Sebastian Sumpf <Sebastian.Sumpf@genode-labs.com>
* \date 2010-07-26
*/
/*
* Copyright (C) 2010-2013 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/printf.h>
#include <base/sleep.h>
#include <base/rpc_server.h>
#include <cap_session/connection.h>
#include <dataspace/client.h>
#include <rom_session/connection.h>
#include <root/component.h>
#include <rm_session/connection.h>
#include <util/avl_string.h>
#include <util/misc_math.h>
/* local includes */
#include "iso9660.h"
#include "backing_store.h"
using namespace Genode;
/*****************
** ROM service **
*****************/
namespace Iso {
typedef Backing_store<Genode::off_t> Backing_store;
typedef Avl_string<PATH_LENGTH> File_base;
/**
* File abstraction
*/
class File : public File_base,
public Signal_context,
public Backing_store::User
{
private:
File_info *_info;
Rm_connection *_rm;
Signal_receiver *_receiver;
Backing_store *_backing_store;
public:
File(char *path, Signal_receiver *receiver, Backing_store *backing_store)
: File_base(path),
_info(Iso::file_info(path)),
_receiver(receiver),
_backing_store(backing_store)
{
size_t rm_size = align_addr(_info->page_sized(),
log2(_backing_store->block_size()));
_rm = new(env()->heap()) Rm_connection(0, rm_size);
_rm->fault_handler(receiver->manage(this));
}
~File()
{
_receiver->dissolve(this);
_backing_store->flush(this);
destroy(env()->heap(), _rm);
destroy(env()->heap(), _info);
}
Rm_connection *rm() { return _rm; }
void handle_fault()
{
Rm_session::State state = _rm->state();
if (verbose)
printf("rm session state is %s, pf_addr=0x%lx\n",
state.type == Rm_session::READ_FAULT ? "READ_FAULT" :
state.type == Rm_session::WRITE_FAULT ? "WRITE_FAULT" :
state.type == Rm_session::EXEC_FAULT ? "EXEC_FAULT" : "READY",
state.addr);
if (state.type == Rm_session::READY)
return;
Backing_store::Block *block = _backing_store->alloc();
/*
* Calculate backing-store-block-aligned file offset from
* page-fault address.
*/
Genode::off_t file_offset = state.addr;
file_offset &= ~(_backing_store->block_size() - 1);
/* re-initialize block content */
Genode::memset(_backing_store->local_addr(block), 0,
_backing_store->block_size());
/* read file content to block */
unsigned long bytes = Iso::read_file(_info, file_offset,
_backing_store->block_size(),
_backing_store->local_addr(block));
if (verbose)
PDBG("[%ld] ATTACH: rm=%p, a=%08lx s=%lx",
_backing_store->index(block), _rm, file_offset, bytes);
bool try_again;
do {
try_again = false;
try {
_rm->attach_at(_backing_store->dataspace(), file_offset,
_backing_store->block_size(),
_backing_store->offset(block)); }
catch (Genode::Rm_session::Region_conflict) {
PERR("Region conflict - this should not happen"); }
catch (Genode::Rm_session::Out_of_metadata) {
/* give up if the error occurred a second time */
if (try_again)
break;
PINF("upgrading quota donation for RM session");
Genode::env()->parent()->upgrade(_rm->cap(), "ram_quota=32K");
try_again = true;
}
} while (try_again);
/*
* Register ourself as user of the block and thereby enable
* future eviction.
*/
_backing_store->assign(block, this, file_offset);
}
/**************************
** File cache interface **
**************************/
/**
* File cache that holds files in order to re-use
* them in different sessions that request already cached files
*/
static Avl_tree<Avl_string_base> *cache()
{
static Avl_tree<Avl_string_base> _avl;
return &_avl;
}
static File *scan_cache(const char *path) {
return static_cast<File *>(cache()->first() ?
cache()->first()->find_by_name(path) :
0);
}
/***********************************
** Backing_store::User interface **
***********************************/
/**
* Called by backing store if block gets evicted
*/
void detach_block(Genode::off_t file_offset)
{
_rm->detach((void *)file_offset);
}
};
class Rom_component : public Genode::Rpc_object<Rom_session>
{
private:
File *_file;
public:
Rom_dataspace_capability dataspace() {
return static_cap_cast<Rom_dataspace>(_file->rm()->dataspace()); }
void sigh(Signal_context_capability) { }
Rom_component(char *path, Signal_receiver *receiver,
Backing_store *backing_store)
{
if ((_file = File::scan_cache(path))) {
PINF("cache hit for file %s", path);
return;
}
_file = new(env()->heap()) File(path, receiver, backing_store);
PINF("request for file %s", path);
File::cache()->insert(_file);
}
};
class Pager : public Thread<8192>
{
private:
Signal_receiver _receiver;
public:
Pager() : Thread("iso_pager") { }
Signal_receiver *signal_receiver() { return &_receiver; }
void entry()
{
while (true) {
try {
Signal signal = _receiver.wait_for_signal();
for (unsigned i = 0; i < signal.num(); i++)
static_cast<File *>(signal.context())->handle_fault();
} catch (...) {
PDBG("unexpected error while waiting for signal");
}
}
}
static Pager* pager()
{
static Pager _pager;
return &_pager;
}
};
typedef Root_component<Rom_component> Root_component;
class Root : public Root_component
{
private:
char _path[PATH_LENGTH];
Backing_store *_backing_store;
protected:
Rom_component *_create_session(const char *args)
{
size_t ram_quota =
Arg_string::find_arg(args, "ram_quota").ulong_value(0);
size_t session_size = sizeof(Rom_component) + sizeof(File_info) + sizeof(Rm_connection);
if (ram_quota < session_size)
throw Root::Quota_exceeded();
Arg_string::find_arg(args,
"filename").string(_path,
sizeof(_path), "");
if (verbose)
PDBG("Request for file %s lrn %zu", _path, strlen(_path));
try {
return new (md_alloc())
Rom_component(_path, Pager::pager()->signal_receiver(),
_backing_store);
}
catch (Io_error) { throw Root::Unavailable(); }
catch (Non_data_disc) { throw Root::Unavailable(); }
catch (File_not_found) { throw Root::Invalid_args(); }
}
public:
Root(Rpc_entrypoint *ep, Allocator *md_alloc,
Backing_store *backing_store)
:
Root_component(ep, md_alloc), _backing_store(backing_store)
{ }
};
}
int main()
{
/*
* XXX this could be a config parameter
*/
const Genode::size_t backing_store_block_size = 8*Iso::PAGE_SIZE;
enum { RESERVED_RAM = 5*1024*1024 };
const Genode::size_t use_ram = env()->ram_session()->avail() - RESERVED_RAM;
static Iso::Backing_store backing_store(use_ram, backing_store_block_size);
/* start pager thread */
Iso::Pager::pager()->start();
/* initialize ROM service */
enum { STACK_SIZE = sizeof(long)*4096 };
static Cap_connection cap;
static Rpc_entrypoint ep(&cap, STACK_SIZE, "iso9660_ep");
static Iso::Root root(&ep, env()->heap(), &backing_store);
env()->parent()->announce(ep.manage(&root));
sleep_forever();
return 0;
}

View File

@ -0,0 +1,3 @@
TARGET = iso9660
SRC_CC = main.cc iso9660.cc
LIBS = base

View File

@ -0,0 +1,5 @@
This directory contains a loader service, which allows for the instantiation of
subsystems via a session interface. The resources for the new subsystem are
provided by the client when opening the session. The client has no control over
the functioning of the subsystem except for the controlling the lifetime of the
subsystem.

View File

@ -0,0 +1,179 @@
/*
* \brief Loader child interface
* \author Christian Prochaska
* \author Norman Feske
* \date 2009-10-05
*/
/*
* Copyright (C) 2009-2013 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 _CHILD_H_
#define _CHILD_H_
/* Genode includes */
#include <base/child.h>
#include <util/arg_string.h>
#include <init/child_policy.h>
#include <ram_session/connection.h>
#include <cpu_session/connection.h>
namespace Loader {
using namespace Genode;
class Child : public Child_policy
{
private:
struct Label {
char string[Session::Name::MAX_SIZE];
Label(char const *l) { strncpy(string, l, sizeof(string)); }
} _label;
Native_pd_args _pd_args;
Rpc_entrypoint &_ep;
struct Resources
{
Ram_connection ram;
Cpu_connection cpu;
Rm_connection rm;
Resources(char const *label,
Ram_session_client &ram_session_client,
size_t ram_quota,
Signal_context_capability fault_sigh)
: ram(label), cpu(label)
{
/* deduce session costs from usable ram quota */
size_t session_donations = Rm_connection::RAM_QUOTA +
Cpu_connection::RAM_QUOTA +
Ram_connection::RAM_QUOTA;
if (ram_quota > session_donations)
ram_quota -= session_donations;
else ram_quota = 0;
ram.ref_account(ram_session_client);
ram_session_client.transfer_quota(ram.cap(), ram_quota);
/*
* Install CPU exception and RM fault handler assigned by
* the loader client via 'Loader_session::fault_handler'.
*/
cpu.exception_handler(Thread_capability(), fault_sigh);
rm.fault_handler(fault_sigh);
}
} _resources;
Service_registry &_parent_services;
Service &_local_nitpicker_service;
Service &_local_rom_service;
Service &_local_cpu_service;
Service &_local_rm_service;
Rom_session_client _binary_rom_session;
Init::Child_policy_provide_rom_file _binary_policy;
Init::Child_policy_enforce_labeling _labeling_policy;
Init::Child_policy_pd_args _pd_args_policy;
Genode::Child _child;
Rom_session_capability _rom_session(char const *name)
{
try {
char args[Session::Name::MAX_SIZE];
snprintf(args, sizeof(args), "ram_quota=4K, filename=\"%s\"", name);
return static_cap_cast<Rom_session>(_local_rom_service.session(args, Affinity()));
} catch (Genode::Parent::Service_denied) {
PERR("Lookup for ROM module \"%s\" failed", name);
throw;
}
}
public:
Child(char const *binary_name,
char const *label,
Native_pd_args const &pd_args,
Rpc_entrypoint &ep,
Ram_session_client &ram_session_client,
size_t ram_quota,
Service_registry &parent_services,
Service &local_rom_service,
Service &local_cpu_service,
Service &local_rm_service,
Service &local_nitpicker_service,
Signal_context_capability fault_sigh)
:
_label(label),
_pd_args(pd_args),
_ep(ep),
_resources(_label.string, ram_session_client, ram_quota, fault_sigh),
_parent_services(parent_services),
_local_nitpicker_service(local_nitpicker_service),
_local_rom_service(local_rom_service),
_local_cpu_service(local_cpu_service),
_local_rm_service(local_rm_service),
_binary_rom_session(_rom_session(binary_name)),
_binary_policy("binary", _binary_rom_session.dataspace(), &_ep),
_labeling_policy(_label.string),
_pd_args_policy(&_pd_args),
_child(_binary_rom_session.dataspace(),
_resources.ram.cap(), _resources.cpu.cap(),
_resources.rm.cap(), &_ep, this)
{ }
~Child()
{
_local_rom_service.close(_binary_rom_session);
}
/****************************
** Child-policy interface **
****************************/
char const *name() const { return _label.string; }
Native_pd_args const *pd_args() const { return &_pd_args; }
void filter_session_args(char const *service, char *args, size_t args_len)
{
_labeling_policy.filter_session_args(service, args, args_len);
_pd_args_policy. filter_session_args(service, args, args_len);
}
Service *resolve_session_request(const char *name,
const char *args)
{
Service *service = 0;
if ((service = _binary_policy.resolve_session_request(name, args)))
return service;
if (!strcmp(name, "Nitpicker")) return &_local_nitpicker_service;
if (!strcmp(name, "ROM")) return &_local_rom_service;
if (!strcmp(name, "CPU")) return &_local_cpu_service;
if (!strcmp(name, "RM")) return &_local_rm_service;
/* populate session-local parent service registry on demand */
service = _parent_services.find(name);
if (!service) {
service = new (env()->heap()) Parent_service(name);
_parent_services.insert(service);
}
return service;
}
};
}
#endif /* _CHILD_H_ */

View File

@ -0,0 +1,103 @@
/*
* \brief Virtualized input session
* \author Christian Prochaska
* \author Norman Feske
* \date 2010-09-02
*
* This input service implementation is used by the virtualized nitpicker
* service to translate the input coordinate system from the coordinates
* seen by the user of the virtualized session and the physical coordinates
* dictated by the loader-session client.
*/
/*
* Copyright (C) 2010-2013 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 _INPUT_H_
#define _INPUT_H_
#include <base/rpc_server.h>
#include <input/event.h>
#include <input/keycodes.h>
#include <input_session/client.h>
namespace Input {
using namespace Genode;
struct Transformer
{
struct Delta { int const x, y; };
virtual Delta delta() = 0;
};
class Session_component : public Rpc_object<Session>
{
private:
Session_client _real_input;
Transformer &_transformer;
Event * const _ev_buf;
public:
/**
* Constructor
*/
Session_component(Session_capability real_input,
Transformer &transformer)
:
_real_input(real_input), _transformer(transformer),
_ev_buf(env()->rm_session()->attach(_real_input.dataspace()))
{ }
/**
* Destructor
*/
~Session_component() { env()->rm_session()->detach(_ev_buf); }
/*****************************
** Input session interface **
*****************************/
Dataspace_capability dataspace() { return _real_input.dataspace(); }
bool is_pending() const { return _real_input.is_pending(); }
int flush()
{
/* translate mouse position to child's coordinate system */
Transformer::Delta delta = _transformer.delta();
int const num_ev = _real_input.flush();
for (int i = 0; i < num_ev; i++) {
Input::Event &ev = _ev_buf[i];
if ((ev.type() == Input::Event::MOTION)
|| (ev.type() == Input::Event::WHEEL)
|| (ev.code() == Input::BTN_LEFT)
|| (ev.code() == Input::BTN_RIGHT)
|| (ev.code() == Input::BTN_MIDDLE)) {
ev = Input::Event(ev.type(),
ev.code(),
ev.ax() - delta.x,
ev.ay() - delta.y,
ev.rx(),
ev.ry());
}
}
return num_ev;
}
};
}
#endif /* _INPUT_H_ */

View File

@ -0,0 +1,429 @@
/*
* \brief Loader service
* \author Norman Feske
* \date 2012-04-17
*/
/*
* Copyright (C) 2010-2013 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/heap.h>
#include <base/rpc_server.h>
#include <base/signal.h>
#include <base/sleep.h>
#include <loader_session/loader_session.h>
#include <cap_session/connection.h>
#include <nitpicker_view/capability.h>
#include <root/component.h>
/* local includes */
#include <child.h>
#include <nitpicker.h>
#include <ram_session_client_guard.h>
#include <rom.h>
namespace Loader {
using namespace Genode;
class Session_component : public Rpc_object<Session>
{
private:
struct Local_rom_service : Service
{
Rpc_entrypoint &_ep;
Allocator &_md_alloc;
Parent_service _parent_rom_service;
Rom_module_registry &_rom_modules;
Lock _lock;
List<Rom_session_component> _rom_sessions;
void _close(Rom_session_component *rom)
{
_ep.dissolve(rom);
_rom_sessions.remove(rom);
destroy(&_md_alloc, rom);
}
Local_rom_service(Rpc_entrypoint &ep,
Allocator &md_alloc,
Rom_module_registry &rom_modules)
:
Service("virtual_rom"),
_ep(ep),
_md_alloc(md_alloc),
_parent_rom_service(Rom_session::service_name()),
_rom_modules(rom_modules)
{ }
~Local_rom_service()
{
Lock::Guard guard(_lock);
while (_rom_sessions.first()) {
_close(_rom_sessions.first()); }
}
Genode::Session_capability session(char const *args,
Affinity const &affinity)
{
/* try to find ROM module at local ROM service */
try {
Lock::Guard guard(_lock);
char name[Session::Name::MAX_SIZE];
/* extract filename from session arguments */
Arg_string::find_arg(args, "filename")
.string(name, sizeof(name), "");
Rom_module &module = _rom_modules.lookup_and_lock(name);
Rom_session_component *rom = new (&_md_alloc)
Rom_session_component(module);
_rom_sessions.insert(rom);
return _ep.manage(rom);
} catch (...) { }
/* fall back to parent_rom_service */
return _parent_rom_service.session(args, affinity);
}
void close(Session_capability session)
{
Lock::Guard guard(_lock);
Rpc_object_base *rom = _ep.lookup_and_lock(session);
Rom_session_component *component =
dynamic_cast<Rom_session_component *>(rom);
if (component) {
_close(component);
return;
}
_parent_rom_service.close(session);
}
void upgrade(Session_capability session, const char *) { }
};
/**
* Common base class of 'Local_cpu_service' and 'Local_rm_service'
*/
struct Intercepted_parent_service : Service
{
Signal_context_capability fault_sigh;
Intercepted_parent_service(char const *name) : Service(name) { }
void close(Session_capability session)
{
env()->parent()->close(session);
}
void upgrade(Session_capability session, const char *) { }
};
/**
* Intercept CPU session requests to install default exception
* handler
*/
struct Local_cpu_service : Intercepted_parent_service
{
Local_cpu_service() : Intercepted_parent_service("CPU") { }
Genode::Session_capability session(char const *args,
Affinity const &affinity)
{
Capability<Cpu_session> cap = env()->parent()->session<Cpu_session>(args, affinity);
Cpu_session_client(cap).exception_handler(Thread_capability(), fault_sigh);
return cap;
}
};
/**
* Intercept RM session requests to install default fault handler
*/
struct Local_rm_service : Intercepted_parent_service
{
Local_rm_service() : Intercepted_parent_service("RM") { }
Genode::Session_capability session(char const *args,
Affinity const &affinity)
{
Capability<Rm_session> cap = env()->parent()->session<Rm_session>(args, affinity);
Rm_session_client(cap).fault_handler(fault_sigh);
return cap;
}
};
struct Local_nitpicker_service : Service
{
Rpc_entrypoint &ep;
Allocator &_md_alloc;
int _max_width;
int _max_height;
Signal_context_capability view_ready_sigh;
Nitpicker::Session_component *open_session;
Local_nitpicker_service(Rpc_entrypoint &ep,
Allocator &md_alloc)
:
Service("virtual_nitpicker"),
ep(ep),
_md_alloc(md_alloc),
_max_width(-1),
_max_height(-1),
open_session(0)
{ }
~Local_nitpicker_service()
{
if (!open_session)
return;
ep.dissolve(open_session);
destroy(&_md_alloc, open_session);
}
void constrain_geometry(int width, int height)
{
_max_width = width, _max_height = height;
}
Genode::Session_capability session(char const *args,
Affinity const &)
{
if (open_session)
throw Unavailable();
open_session = new (&_md_alloc)
Nitpicker::Session_component(ep,
_max_width,
_max_height,
view_ready_sigh,
args);
return ep.manage(open_session);
}
void upgrade(Genode::Session_capability session, const char *) { }
};
enum { STACK_SIZE = 2*4096 };
size_t _ram_quota;
Ram_session_client_guard _ram_session_client;
Heap _md_alloc;
size_t _subsystem_ram_quota_limit;
Rpc_entrypoint _ep;
Service_registry _parent_services;
Rom_module_registry _rom_modules;
Local_rom_service _rom_service;
Local_cpu_service _cpu_service;
Local_rm_service _rm_service;
Local_nitpicker_service _nitpicker_service;
Signal_context_capability _fault_sigh;
Child *_child;
/**
* Return virtual nitpicker session component
*/
Nitpicker::Session_component *_virtual_nitpicker_session()
{
if (!_nitpicker_service.open_session)
throw View_does_not_exist();
return _nitpicker_service.open_session;
}
public:
/**
* Constructor
*/
Session_component(size_t quota, Ram_session &ram, Cap_session &cap)
:
_ram_quota(quota),
_ram_session_client(env()->ram_session_cap(), _ram_quota),
_md_alloc(&_ram_session_client, env()->rm_session()),
_subsystem_ram_quota_limit(0),
_ep(&cap, STACK_SIZE, "session_ep"),
_rom_modules(_ram_session_client, _md_alloc),
_rom_service(_ep, _md_alloc, _rom_modules),
_nitpicker_service(_ep, _md_alloc),
_child(0)
{ }
~Session_component()
{
if (_child)
destroy(&_md_alloc, _child);
/*
* The parent-service registry is populated by the 'Child'
* on demand. Revert those allocations.
*/
while (Service *service = _parent_services.find_by_server(0)) {
_parent_services.remove(service);
destroy(env()->heap(), service);
}
}
/******************************
** Loader session interface **
******************************/
Dataspace_capability alloc_rom_module(Name const &name, size_t size)
{
return _rom_modules.alloc_rom_module(name.string(), size);
}
void commit_rom_module(Name const &name)
{
try {
_rom_modules.commit_rom_module(name.string()); }
catch (Rom_module_registry::Lookup_failed) {
throw Rom_module_does_not_exist(); }
}
void ram_quota(size_t quantum)
{
_subsystem_ram_quota_limit = quantum;
}
void constrain_geometry(int width, int height)
{
_nitpicker_service.constrain_geometry(width, height);
}
void view_ready_sigh(Signal_context_capability sigh)
{
_nitpicker_service.view_ready_sigh = sigh;
}
void fault_sigh(Signal_context_capability sigh)
{
/*
* CPU exception handler for CPU sessions originating from the
* subsystem.
*/
_cpu_service.fault_sigh = sigh;
/*
* RM fault handler for RM sessions originating from the
* subsystem.
*/
_rm_service.fault_sigh = sigh;
/*
* CPU exception and RM fault handler for the immediate child.
*/
_fault_sigh = sigh;
}
void start(Name const &binary_name, Name const &label,
Genode::Native_pd_args const &pd_args)
{
if (_child) {
PWRN("cannot start subsystem twice");
return;
}
size_t const ram_quota = (_subsystem_ram_quota_limit > 0) ?
min(_subsystem_ram_quota_limit, _ram_session_client.avail()) :
_ram_session_client.avail();
try {
_child = new (&_md_alloc)
Child(binary_name.string(), label.string(),
pd_args, _ep, _ram_session_client,
ram_quota, _parent_services, _rom_service,
_cpu_service, _rm_service, _nitpicker_service,
_fault_sigh);
}
catch (Genode::Parent::Service_denied) {
throw Rom_module_does_not_exist(); }
}
Nitpicker::View_capability view()
{
return _virtual_nitpicker_session()->loader_view();
}
View_geometry view_geometry()
{
return _virtual_nitpicker_session()->loader_view_geometry();
}
};
class Root : public Root_component<Session_component>
{
private:
Ram_session &_ram;
Cap_session &_cap;
protected:
Session_component *_create_session(const char *args)
{
size_t quota =
Arg_string::find_arg(args, "ram_quota").long_value(0);
return new (md_alloc()) Session_component(quota, _ram, _cap);
}
public:
/**
* Constructor
*
* \param session_ep entry point for managing ram session objects
* \param md_alloc meta-data allocator to be used by root
* component
*/
Root(Rpc_entrypoint &session_ep, Allocator &md_alloc,
Ram_session &ram, Cap_session &cap)
:
Root_component<Session_component>(&session_ep, &md_alloc),
_ram(ram), _cap(cap)
{ }
};
}
int main()
{
using namespace Genode;
enum { STACK_SIZE = 8*1024 };
static Cap_connection cap;
static Rpc_entrypoint ep(&cap, STACK_SIZE, "loader_ep");
static Loader::Root root(ep, *env()->heap(), *env()->ram_session(), cap);
env()->parent()->announce(ep.manage(&root));
sleep_forever();
return 0;
}

View File

@ -0,0 +1,310 @@
/*
* \brief Virtualized nitpicker session interface exposed to the subsystem
* \author Christian Prochaska
* \author Norman Feske
* \date 2009-10-05
*/
/*
* Copyright (C) 2009-2013 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 _NITPICKER_H_
#define _NITPICKER_H_
/* Genode includes */
#include <util/arg_string.h>
#include <util/misc_math.h>
#include <base/signal.h>
#include <nitpicker_session/connection.h>
#include <nitpicker_session/nitpicker_session.h>
#include <nitpicker_view/client.h>
/* local includes */
#include <input.h>
namespace Nitpicker {
using namespace Genode;
/**
* View interface provided to the loader client
*/
class Loader_view_component : public Rpc_object<View>
{
private:
View_client _view; /* wrapped view */
int _x, _y, _buf_x, _buf_y;
public:
/**
* Constructor
*/
Loader_view_component(View_capability view_cap)
:
_view(view_cap), _x(0), _y(0), _buf_x(0), _buf_y(0)
{ }
int x() const { return _x; }
int y() const { return _y; }
int buf_x() const { return _buf_x; }
int buf_y() const { return _buf_y; }
/******************************
** Nitpicker view interface **
******************************/
int viewport(int x, int y, int w, int h,
int buf_x, int buf_y, bool redraw)
{
_x = x, _y = y, _buf_x = buf_x, _buf_y = buf_y;
return _view.viewport(x, y, w, h, buf_x, buf_y, redraw);
}
int stack(View_capability neighbor_cap, bool behind, bool redraw)
{
return _view.stack(neighbor_cap, behind, redraw);
}
int title(Title const &title)
{
return _view.title(title.string());
}
};
/**
* View interface exposed to the subsystem
*/
class View_component : public Rpc_object<View>
{
private:
View_client _view; /* wrapped view */
Signal_context_capability _sigh;
bool _viewport_initialized;
int _x, _y, _w, _h, _buf_x, _buf_y;
public:
/**
* Constructor
*/
View_component(View_capability view_cap,
Signal_context_capability sigh)
:
_view(view_cap), _sigh(sigh), _viewport_initialized(false),
_x(0), _y(0), _w(0), _h(0), _buf_x(0), _buf_y(0)
{ }
int x() const { return _x; }
int y() const { return _y; }
int w() const { return _w; }
int h() const { return _h; }
int buf_x() const { return _buf_x; }
int buf_y() const { return _buf_y; }
/******************************
** Nitpicker view interface **
******************************/
int viewport(int x, int y, int w, int h,
int buf_x, int buf_y, bool redraw)
{
_x = x; _y = y; _w = w; _h = h;
_buf_x = buf_x; _buf_y = buf_y;
if (_viewport_initialized)
return 0;
_viewport_initialized = true;
/* hide the view and let the application set the viewport */
int result = _view.viewport(0, 0, 0, 0, 0, 0, true);
/* signal readyness of the view */
if (_sigh.valid())
Signal_transmitter(_sigh).submit();
return result;
}
int stack(View_capability neighbor_cap, bool behind, bool redraw)
{
/*
* Only one child view is supported, so the stack request can
* be ignored.
*/
return 0;
}
int title(Title const &title)
{
return _view.title(title.string());
}
};
class Session_component : public Rpc_object<Session>,
public Input::Transformer
{
private:
Rpc_entrypoint &_ep;
int _max_width;
int _max_height;
Nitpicker::Connection _nitpicker;
View_capability _nitpicker_view;
View_component _proxy_view;
View_capability _proxy_view_cap;
Loader_view_component _loader_view;
View_capability _loader_view_cap;
Input::Session_component _proxy_input;
Input::Session_capability _proxy_input_cap;
static long _session_arg(const char *arg, const char *key) {
return Arg_string::find_arg(arg, key).long_value(0); }
public:
/**
* Constructor
*/
Session_component(Rpc_entrypoint &ep,
int max_width,
int max_height,
Signal_context_capability view_ready_sigh,
const char *args)
:
_ep(ep),
_max_width(max_width),
_max_height(max_height),
/* create Nitpicker view */
_nitpicker_view(_nitpicker.create_view()),
/* create proxy view component for the child */
_proxy_view(_nitpicker_view, view_ready_sigh),
_proxy_view_cap(_ep.manage(&_proxy_view)),
/* create view component accessed by the loader client */
_loader_view(_nitpicker_view),
_loader_view_cap(_ep.manage(&_loader_view)),
_proxy_input(_nitpicker.input_session(), *this),
_proxy_input_cap(_ep.manage(&_proxy_input))
{ }
/*********************************
** Nitpicker session interface **
*********************************/
Framebuffer::Session_capability framebuffer_session()
{
return _nitpicker.framebuffer_session();
}
Input::Session_capability input_session()
{
return _proxy_input_cap;
}
View_capability create_view(View_capability)
{
return _proxy_view_cap;
}
void destroy_view(View_capability view)
{
_nitpicker.destroy_view(_nitpicker_view);
}
int background(View_capability view)
{
/* not forwarding to real nitpicker session */
return 0;
}
Framebuffer::Mode mode()
{
int mode_width = (_max_width > -1) ?
_max_width :
_nitpicker.mode().width();
int mode_height = (_max_height > -1) ?
_max_height :
_nitpicker.mode().height();
return Framebuffer::Mode(mode_width, mode_height,
_nitpicker.mode().format());
}
void buffer(Framebuffer::Mode mode, bool use_alpha)
{
_nitpicker.buffer(mode, use_alpha);
}
/**********************************
** Input::Transformer interface **
**********************************/
Input::Transformer::Delta delta()
{
/* translate mouse position to child's coordinate system */
Input::Transformer::Delta result = {
_loader_view.x() + _loader_view.buf_x() +
_proxy_view.x() + _proxy_view.buf_x(),
_loader_view.y() + _loader_view.buf_y() +
_proxy_view.y() + _proxy_view.buf_y() };
return result;
}
/**
* Return the client-specific wrapper view for the Nitpicker view
* showing the child content
*/
View_capability loader_view() { return _loader_view_cap; }
/**
* Return geometry of loader view
*/
Loader::Session::View_geometry loader_view_geometry()
{
int view_width = (_max_width > -1) ?
min(_proxy_view.w(), _max_width) :
_proxy_view.w();
int view_height = (_max_height > -1) ?
min(_proxy_view.h(), _max_height) :
_proxy_view.h();
Loader::Session::View_geometry result(
view_width,
view_height,
_proxy_view.buf_x(),
_proxy_view.buf_y());
return result;
}
};
}
#endif /* _NITPICKER_H_ */

View File

@ -0,0 +1,95 @@
/*
* \brief A guard for RAM session clients to limit memory exhaustion
* \author Christian Prochaska
* \date 2012-04-25
*/
/*
* Copyright (C) 2012-2013 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 _RAM_SESSION_CLIENT_GUARD_H_
#define _RAM_SESSION_CLIENT_GUARD_H_
#include <base/lock.h>
#include <base/printf.h>
#include <dataspace/client.h>
#include <ram_session/client.h>
namespace Genode {
class Ram_session_client_guard : public Ram_session_client
{
private:
size_t _amount; /* total amount */
size_t _consumed; /* already consumed bytes */
Lock _consumed_lock;
public:
Ram_session_client_guard(Ram_session_capability session, size_t amount)
: Ram_session_client(session), _amount(amount), _consumed(0) { }
Ram_dataspace_capability alloc(size_t size, bool cached)
{
Lock::Guard _consumed_lock_guard(_consumed_lock);
if ((_amount - _consumed) < size) {
PWRN("Quota exceeded! amount=%zu, size=%zu, consumed=%zu",
_amount, size, _consumed);
return Ram_dataspace_capability();
}
Ram_dataspace_capability cap =
Ram_session_client::alloc(size, cached);
_consumed += size;
return cap;
}
void free(Ram_dataspace_capability ds)
{
Lock::Guard _consumed_lock_guard(_consumed_lock);
_consumed -= Dataspace_client(ds).size();
Ram_session_client::free(ds);
}
int transfer_quota(Ram_session_capability ram_session, size_t amount)
{
Lock::Guard _consumed_lock_guard(_consumed_lock);
if ((_amount - _consumed) < amount) {
PWRN("Quota exceeded! amount=%zu, size=%zu, consumed=%zu",
_amount, amount, _consumed);
return -1;
}
int result = Ram_session_client::transfer_quota(ram_session, amount);
if (result == 0)
_consumed += amount;
return result;
}
size_t quota()
{
return _amount;
}
size_t used()
{
Lock::Guard _consumed_lock_guard(_consumed_lock);
return _consumed;
}
};
}
#endif /* _RAM_SESSION_CLIENT_GUARD_H_ */

View File

@ -0,0 +1,244 @@
/*
* \brief ROM service exposed to the loaded subsystem
* \author Norman Feske
* \date 2012-04-17
*/
#ifndef _ROM_H_
#define _ROM_H_
//#include <util/string.h>
#include <rom_session/rom_session.h>
#include <base/rpc_server.h>
#include <os/attached_ram_dataspace.h>
namespace Genode {
class Rom_module : public List<Rom_module>::Element
{
private:
enum { MAX_NAME_LEN = 64 };
char _name[MAX_NAME_LEN];
Ram_session &_ram;
Attached_ram_dataspace _fg;
Attached_ram_dataspace _bg;
bool _bg_has_pending_data;
Signal_context_capability _sigh;
Lock _lock;
public:
Rom_module(char const *name, Ram_session &ram_session)
:
_ram(ram_session),
_fg(&_ram, 0), _bg(&_ram, 0),
_bg_has_pending_data(false),
_lock(Lock::LOCKED)
{
strncpy(_name, name, sizeof(_name));
}
bool has_name(char const *name) const
{
return strcmp(_name, name) == 0;
}
void lock() { _lock.lock(); }
void unlock() { _lock.unlock(); }
/**
* Return dataspace as handed out to loader session
*
* The loader session submits the data into the ROM module into
* the background dataspace. Once it is ready, the 'commit_bg()'
* function is called.
*/
Dataspace_capability bg_dataspace(size_t size)
{
/* let background buffer grow if needed */
if (_bg.size() < size)
_bg.realloc(&_ram, size);
return _bg.cap();
}
/**
* Return dataspace as handed out to ROM session client
*/
Rom_dataspace_capability fg_dataspace()
{
if (!_fg.size() && !_bg_has_pending_data) {
PERR("Error: no data loaded");
return Rom_dataspace_capability();
}
/*
* Keep foreground if no background exists. Otherwise, use
* old background as new foreground.
*/
if (_bg_has_pending_data) {
_fg.swap(_bg);
_bg_has_pending_data = false;
}
Dataspace_capability ds_cap = _fg.cap();
return static_cap_cast<Rom_dataspace>(ds_cap);
}
/**
* Set signal handler to inform about ROM module updates
*
* This function is indirectly called by the ROM session client.
*/
void sigh(Signal_context_capability sigh) { _sigh = sigh; }
/**
* Commit data contained in background dataspace
*
* This function is indirectly called by the loader session.
*/
void commit_bg()
{
_bg_has_pending_data = true;
if (_sigh.valid())
Signal_transmitter(_sigh).submit();
}
};
struct Rom_module_lock_guard
{
Rom_module &rom;
Rom_module_lock_guard(Rom_module &rom) : rom(rom) { }
~Rom_module_lock_guard() { rom.unlock(); }
};
class Rom_module_registry
{
private:
Lock _lock;
Ram_session &_ram_session;
Allocator &_md_alloc;
List<Rom_module> _list;
public:
/**
* Exception type
*/
class Lookup_failed { };
/**
* Constructor
*
* \param ram_session RAM session used as backing store for
* module data
* \param md_alloc backing store for ROM module meta data
*/
Rom_module_registry(Ram_session &ram_session, Allocator &md_alloc)
:
_ram_session(ram_session), _md_alloc(md_alloc)
{ }
~Rom_module_registry()
{
Lock::Guard guard(_lock);
while (_list.first()) {
Rom_module *rom = _list.first();
rom->lock();
_list.remove(rom);
destroy(&_md_alloc, rom);
}
}
/**
* Lookup ROM module by name
*
* \throw Lookup_failed
*/
Rom_module &lookup_and_lock(char const *name)
{
Lock::Guard guard(_lock);
Rom_module *curr = _list.first();
for (; curr; curr = curr->next()) {
if (curr->has_name(name)) {
curr->lock();
return *curr;
}
}
throw Lookup_failed();
}
Dataspace_capability alloc_rom_module(char const *name, size_t size)
{
try {
Rom_module &module = lookup_and_lock(name);
Rom_module_lock_guard guard(module);
return module.bg_dataspace(size);
}
catch (Lookup_failed) {
Lock::Guard guard(_lock);
Rom_module *module = new (&_md_alloc)
Rom_module(name, _ram_session);
Rom_module_lock_guard module_guard(*module);
_list.insert(module);
return module->bg_dataspace(size);
}
}
/**
* \throw Lookup_failed
*/
void commit_rom_module(char const *name)
{
Rom_module &rom_module = lookup_and_lock(name);
rom_module.commit_bg();
rom_module.unlock();
}
};
class Rom_session_component : public Rpc_object<Rom_session>,
public List<Rom_session_component>::Element
{
private:
Rom_module &_rom_module;
public:
Rom_session_component(Rom_module &rom_module)
: _rom_module(rom_module) { }
Rom_dataspace_capability dataspace()
{
Rom_module_lock_guard guard(_rom_module);
return _rom_module.fg_dataspace();
}
void sigh(Signal_context_capability sigh)
{
Rom_module_lock_guard guard(_rom_module);
_rom_module.sigh(sigh);
}
};
}
#endif /* _ROM_H_ */

View File

@ -0,0 +1,4 @@
TARGET = loader
LIBS = base init_pd_args
SRC_CC = main.cc
INC_DIR += $(PRG_DIR)

View File

@ -0,0 +1,128 @@
/*
* \brief Report server that dumps reports to the LOG
* \author Norman Feske
* \date 2014-01-10
*/
/*
* Copyright (C) 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 <report_session/report_session.h>
#include <util/arg_string.h>
#include <base/heap.h>
#include <base/env.h>
#include <root/component.h>
#include <os/server.h>
#include <os/attached_ram_dataspace.h>
namespace Report {
using Server::Entrypoint;
using Genode::env;
class Session_component;
class Root;
struct Main;
}
class Report::Session_component : public Genode::Rpc_object<Session>
{
public:
typedef Genode::String<200> Label;
private:
Label _label;
Genode::Attached_ram_dataspace _ds;
public:
Session_component(Label const &label, size_t buffer_size)
:
_label(label), _ds(env()->ram_session(), buffer_size)
{ }
Dataspace_capability dataspace() override { return _ds.cap(); }
void submit(size_t const length) override
{
using namespace Genode;
printf("\nreport: %s\n", _label.string());
char buf[1024];
for (size_t consumed = 0; consumed < length; consumed += strlen(buf)) {
strncpy(buf, _ds.local_addr<char>() + consumed, sizeof(buf));
printf("%s", buf);
}
printf("\nend of report\n");
}
void response_sigh(Genode::Signal_context_capability) override { }
size_t obtain_response() override { return 0; }
};
class Report::Root : public Genode::Root_component<Session_component>
{
protected:
Session_component *_create_session(const char *args) override
{
using namespace Genode;
/* read label from session arguments */
char label[200];
Arg_string::find_arg(args, "label").string(label, sizeof(label), "");
/* read report buffer size from session arguments */
size_t const buffer_size =
Arg_string::find_arg(args, "buffer_size").long_value(0);
return new (md_alloc())
Session_component(Session_component::Label(label), buffer_size);
}
public:
Root(Entrypoint &ep, Genode::Allocator &md_alloc)
:
Genode::Root_component<Session_component>(&ep.rpc_ep(), &md_alloc)
{ }
};
struct Report::Main
{
Entrypoint &ep;
Genode::Sliced_heap sliced_heap = { env()->ram_session(),
env()->rm_session() };
Root root = { ep, sliced_heap };
Main(Entrypoint &ep) : ep(ep)
{
env()->parent()->announce(ep.manage(root));
}
};
namespace Server {
char const *name() { return "log_report_ep"; }
size_t stack_size() { return 4*1024*sizeof(long); }
void construct(Entrypoint &ep)
{
static Report::Main main(ep);
}
}

View File

@ -0,0 +1,3 @@
TARGET = log_report
SRC_CC = main.cc
LIBS = base server

View File

@ -0,0 +1,195 @@
/*
* \brief Terminal that directs output to the LOG interface
* \author Norman Feske
* \date 2013-11-08
*/
/*
* Copyright (C) 2013 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 <root/component.h>
#include <os/server.h>
#include <os/attached_ram_dataspace.h>
#include <terminal_session/terminal_session.h>
#include <log_session/log_session.h>
namespace Terminal {
class Session_component;
class Root_component;
class Main;
using namespace Genode;
};
/**
* Utility for the buffered output of small successive write operations
*/
class Buffered_output
{
private:
enum { SIZE = Genode::Log_session::String::MAX_SIZE };
typedef Genode::size_t size_t;
char _buf[SIZE + 1 /* room for null-termination */ ];
/* index of next character within '_buf' to write */
unsigned _index = 0;
void _flush()
{
/* append null termination */
_buf[_index] = 0;
/* flush buffered characters to LOG */
Genode::printf(_buf);
/* reset */
_index = 0;
}
size_t _remaining_capacity() const { return SIZE - _index; }
public:
size_t write(char const *src, size_t num_bytes)
{
size_t const consume_bytes = Genode::min(num_bytes,
_remaining_capacity());
for (unsigned i = 0; i < consume_bytes; i++) {
char const c = src[i];
_buf[_index++] = c;
if (c == '\n')
_flush();
}
if (_remaining_capacity() == 0)
_flush();
return consume_bytes;
}
};
/**************
** Terminal **
**************/
class Terminal::Session_component : public Rpc_object<Session, Session_component>
{
private:
/**
* Buffer shared with the terminal client
*/
Attached_ram_dataspace _io_buffer;
Buffered_output _output;
public:
Session_component(size_t io_buffer_size)
:
_io_buffer(env()->ram_session(), io_buffer_size)
{ }
/********************************
** Terminal session interface **
********************************/
Size size() { return Size(0, 0); }
bool avail() { return false; }
size_t _read(size_t dst_len) { return 0; }
void _write(Genode::size_t num_bytes)
{
/* sanitize argument */
num_bytes = Genode::min(num_bytes, _io_buffer.size());
char const *src = _io_buffer.local_addr<char>();
for (size_t written_bytes = 0; written_bytes < num_bytes; )
written_bytes += _output.write(src + written_bytes,
num_bytes - written_bytes);
}
Dataspace_capability _dataspace() { return _io_buffer.cap(); }
void read_avail_sigh(Signal_context_capability) { }
void connected_sigh(Signal_context_capability sigh)
{
/*
* Immediately reflect connection-established signal to the
* client because the session is ready to use immediately after
* creation.
*/
Signal_transmitter(sigh).submit();
}
size_t read(void *buf, size_t) { return 0; }
size_t write(void const *buf, size_t) { return 0; }
};
class Terminal::Root_component : public Genode::Root_component<Session_component>
{
protected:
Session_component *_create_session(const char *args)
{
size_t const io_buffer_size = 4096;
return new (md_alloc()) Session_component(io_buffer_size);
}
public:
Root_component(Server::Entrypoint &ep, Genode::Allocator &md_alloc)
:
Genode::Root_component<Session_component>(&ep.rpc_ep(), &md_alloc)
{ }
};
struct Terminal::Main
{
Server::Entrypoint &ep;
Sliced_heap sliced_heap = { env()->ram_session(), env()->rm_session() };
Root_component terminal_root = { ep, sliced_heap };
Main(Server::Entrypoint &ep) : ep(ep)
{
env()->parent()->announce(ep.manage(terminal_root));
}
};
/************
** Server **
************/
namespace Server {
char const *name() { return "log_terminal_ep"; }
size_t stack_size() { return 4*1024*sizeof(long); }
void construct(Entrypoint &ep)
{
static Terminal::Main terminal(ep);
}
}

View File

@ -0,0 +1,3 @@
TARGET = log_terminal
SRC_CC = main.cc
LIBS = base server

View File

@ -0,0 +1,44 @@
This directory contains an Genode file-system service to Linux host fs
wrapper.
Configuration
~~~~~~~~~~~~~
Access to the file system can be tailored for each session depending on the
session's label. By default, no permissions are granted to any session.
To selectively permit access to (a part of) the file system, at least one
policy must be defined.
The following configuration illustates the way of how to express policy.
! <config>
! <!-- constrain sessions according to their labels -->
! <policy label="noux -> root" root="/" />
! <policy label="noux -> home" root="/home/user" writeable="yes" />
! <policy label="noux -> tmp" root="/tmp" writeable="yes" />
! </config>
Session-specific access-control policy is expressed via one or more '<policy>'
nodes. At session-creation time, each policy node is matched against the label
of the new session. If the label of a policy node matches, the defined policy
is applied. If multiple policies match, the one with the longest 'label'
attribute (the most specific one) is selected.
A policy node may contain the following attributes. The mandatory 'root'
attribute defines the viewport of the session onto the file system. The
optional 'writeable' attribute grants the permission to modify the file system.
Example
~~~~~~~
To illustrate the use of lx_fs, refer to the 'base-linux/run/lx_fs.run'
script.
Notes
~~~~~
If the Linux file system experiences changes from other processes
'inotify' may help to keep the servers cache up-to-date. This is not
implemented yet.

View File

@ -0,0 +1,188 @@
/*
* \brief File-system directory node
* \author Norman Feske
* \author Christian Helmuth
* \date 2013-11-11
*/
#ifndef _DIRECTORY_H_
#define _DIRECTORY_H_
/* Genode include */
#include <os/path.h>
/* local includes */
#include <node.h>
#include <util.h>
#include <file.h>
#include <lx_util.h>
namespace File_system {
class Directory;
}
class File_system::Directory : public Node
{
private:
typedef Genode::Path<MAX_PATH_LEN> Path;
DIR *_fd;
Path _path;
Allocator &_alloc;
unsigned long _inode(char const *path, bool create)
{
int ret;
if (create) {
mode_t ugo = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
ret = mkdir(path, ugo);
if (ret == -1)
throw No_space();
}
struct stat s;
ret = lstat(path, &s);
if (ret == -1)
throw Lookup_failed();
return s.st_ino;
}
DIR *_open(char const *path)
{
DIR *fd = opendir(path);
if (!fd)
throw Lookup_failed();
return fd;
}
public:
Directory(Allocator &alloc, char const *path, bool create)
:
Node(_inode(path, create)),
_fd(_open(path)),
_path(path, "./"),
_alloc(alloc)
{
Node::name(basename(path));
}
virtual ~Directory()
{
closedir(_fd);
}
/* FIXME returned file node must be locked */
File * file(char const *name, Mode mode, bool create)
{
File *file = new (&_alloc) File(dirfd(_fd), name, mode, create);
file->lock();
return file;
}
/* FIXME returned directory node must be locked */
Directory * subdir(char const *path, bool create)
{
Path dir_path(path, _path.base());
Directory *dir = new (&_alloc) Directory(_alloc, dir_path.base(), create);
dir->lock();
return dir;
}
Node * node(char const *path)
{
Path node_path(path, _path.base());
/*
* XXX Currently, symlinks are transparently dereferenced by the
* use of stat(). For symlink detection we would need lstat()
* and implement special handling of the root, which may be a
* link!
*/
struct stat s;
int ret = stat(node_path.base(), &s);
if (ret == -1)
throw Lookup_failed();
Node *node = 0;
if (S_ISDIR(s.st_mode))
node = new (&_alloc) Directory(_alloc, node_path.base(), false);
else if (S_ISREG(s.st_mode))
node = new (&_alloc) File(node_path.base(), STAT_ONLY);
else
throw Lookup_failed();
node->lock();
return node;
}
size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
if (len < sizeof(Directory_entry)) {
PERR("read buffer too small for directory entry");
return 0;
}
if (seek_offset % sizeof(Directory_entry)) {
PERR("seek offset not aligned to sizeof(Directory_entry)");
return 0;
}
seek_off_t index = seek_offset / sizeof(Directory_entry);
/* seek to index and read entry */
struct dirent *dent;
rewinddir(_fd);
for (unsigned i = 0; i <= index; ++i) {
dent = readdir(_fd);
}
if (!dent)
return 0;
Directory_entry *e = (Directory_entry *)(dst);
switch (dent->d_type) {
case DT_REG: e->type = Directory_entry::TYPE_FILE; break;
case DT_DIR: e->type = Directory_entry::TYPE_DIRECTORY; break;
case DT_LNK: e->type = Directory_entry::TYPE_SYMLINK; break;
default:
return 0;
}
strncpy(e->name, dent->d_name, sizeof(e->name));
return sizeof(Directory_entry);
}
size_t write(char const *src, size_t len, seek_off_t seek_offset)
{
/* writing to directory nodes is not supported */
return 0;
}
size_t num_entries() const
{
unsigned num = 0;
rewinddir(_fd);
while (readdir(_fd)) ++num;
return num;
}
};
#endif /* _DIRECTORY_H_ */

View File

@ -0,0 +1,138 @@
/*
* \brief File node
* \author Norman Feske
* \author Christian Helmuth
* \date 2013-11-11
*/
#ifndef _FILE_H_
#define _FILE_H_
/* local includes */
#include <node.h>
#include <lx_util.h>
namespace File_system {
class File;
}
class File_system::File : public Node
{
private:
int _fd;
unsigned long _inode(int dir, char const *name, bool create)
{
int ret;
if (create) {
mode_t ugo = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
ret = mknodat(dir, name, S_IFREG | ugo, 0);
if (ret == -1 && errno != EEXIST)
throw No_space();
}
struct stat s;
ret = fstatat(dir, name, &s, 0);
if (ret == -1)
throw Lookup_failed();
return s.st_ino;
}
unsigned long _inode_path(char const *path)
{
int ret;
struct stat s;
ret = stat(path, &s);
if (ret == -1)
throw Lookup_failed();
return s.st_ino;
}
int _open(int dir, char const *name, Mode mode)
{
int fd = openat(dir, name, access_mode(mode));
if (fd == -1)
throw Lookup_failed();
return fd;
}
int _open_path(char const *path, Mode mode)
{
int fd = open(path, access_mode(mode));
if (fd == -1)
throw Lookup_failed();
return fd;
}
public:
File(int dir,
char const *name,
Mode mode,
bool create)
:
Node(_inode(dir, name, create)),
_fd(_open(dir, name, mode))
{
Node::name(name);
}
File(char const *path, Mode mode)
:
Node(_inode_path(path)),
_fd(_open_path(path, mode))
{
Node::name(basename(path));
}
size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
int ret = pread(_fd, dst, len, seek_offset);
return ret == -1 ? 0 : ret;
}
size_t write(char const *src, size_t len, seek_off_t seek_offset)
{
/* should we append? */
if (seek_offset == ~0ULL) {
::off_t off = lseek(_fd, 0, SEEK_END);
if (off == -1)
return 0;
seek_offset = off;
}
int ret = pwrite(_fd, src, len, seek_offset);
return ret == -1 ? 0 : ret;
}
file_size_t length() const
{
struct stat s;
if (fstat(_fd, &s) < 0)
return 0;
return s.st_size;
}
void truncate(file_size_t size)
{
ftruncate(_fd, size);
mark_as_updated();
}
};
#endif /* _FILE_H_ */

View File

@ -0,0 +1,39 @@
/*
* \brief Linux utilities
* \author Christian Helmuth
* \date 2013-11-11
*/
#ifndef _LX_UTIL_H_
#define _LX_UTIL_H_
/* Genode includes */
#include <file_system_session/file_system_session.h>
/* Linux includes */
#define _FILE_OFFSET_BITS 64
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/stat.h>
namespace File_system {
int access_mode(File_system::Mode const &mode);
}
int File_system::access_mode(File_system::Mode const &mode)
{
switch (mode) {
case STAT_ONLY:
case READ_ONLY: return O_RDONLY;
case WRITE_ONLY: return O_WRONLY;
case READ_WRITE: return O_RDWR;
}
return O_RDONLY;
}
#endif

View File

@ -0,0 +1,441 @@
/*
* \brief RAM file system
* \author Norman Feske
* \date 2012-04-11
*/
/*
* Copyright (C) 2012-2013 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 <file_system_session/rpc_object.h>
#include <root/component.h>
#include <os/attached_rom_dataspace.h>
#include <os/config.h>
#include <os/server.h>
#include <os/session_policy.h>
#include <util/xml_node.h>
/* local includes */
#include <directory.h>
#include <node_handle_registry.h>
namespace File_system {
struct Main;
struct Session_component;
struct Root;
}
class File_system::Session_component : public Session_rpc_object
{
private:
Server::Entrypoint &_ep;
Allocator &_md_alloc;
Directory &_root;
Node_handle_registry _handle_registry;
bool _writable;
Signal_rpc_member<Session_component> _process_packet_dispatcher;
/******************************
** Packet-stream processing **
******************************/
/**
* Perform packet operation
*
* \return true on success, false on failure
*/
void _process_packet_op(Packet_descriptor &packet, Node &node)
{
void * const content = tx_sink()->packet_content(packet);
size_t const length = packet.length();
seek_off_t const offset = packet.position();
if (!content || (packet.length() > packet.size())) {
packet.succeeded(false);
return;
}
/* resulting length */
size_t res_length = 0;
switch (packet.operation()) {
case Packet_descriptor::READ:
res_length = node.read((char *)content, length, offset);
break;
case Packet_descriptor::WRITE:
res_length = node.write((char const *)content, length, offset);
break;
}
packet.length(res_length);
packet.succeeded(res_length > 0);
}
void _process_packet()
{
Packet_descriptor packet = tx_sink()->get_packet();
/* assume failure by default */
packet.succeeded(false);
try {
Node *node = _handle_registry.lookup_and_lock(packet.handle());
Node_lock_guard guard(*node);
_process_packet_op(packet, *node);
}
catch (Invalid_handle) { PERR("Invalid_handle"); }
catch (Size_limit_reached) { PERR("Size_limit_reached"); }
/*
* The 'acknowledge_packet' function cannot block because we
* checked for 'ready_to_ack' in '_process_packets'.
*/
tx_sink()->acknowledge_packet(packet);
}
/**
* Called by signal dispatcher, executed in the context of the main
* thread (not serialized with the RPC functions)
*/
void _process_packets(unsigned)
{
while (tx_sink()->packet_avail()) {
/*
* Make sure that the '_process_packet' function does not
* block.
*
* If the acknowledgement queue is full, we defer packet
* processing until the client processed pending
* acknowledgements and thereby emitted a ready-to-ack
* signal. Otherwise, the call of 'acknowledge_packet()'
* in '_process_packet' would infinitely block the context
* of the main thread. The main thread is however needed
* for receiving any subsequent 'ready-to-ack' signals.
*/
if (!tx_sink()->ready_to_ack())
return;
_process_packet();
}
}
/**
* Check if string represents a valid path (must start with '/')
*/
static void _assert_valid_path(char const *path)
{
if (!path || path[0] != '/') {
PWRN("malformed path '%s'", path);
throw Lookup_failed();
}
}
public:
/**
* Constructor
*/
Session_component(size_t tx_buf_size,
Server::Entrypoint &ep,
char const *root_dir,
bool writable,
Allocator &md_alloc)
:
Session_rpc_object(env()->ram_session()->alloc(tx_buf_size), ep.rpc_ep()),
_ep(ep),
_md_alloc(md_alloc),
_root(*new (&_md_alloc) Directory(_md_alloc, root_dir, false)),
_writable(writable),
_process_packet_dispatcher(ep, *this, &Session_component::_process_packets)
{
/*
* Register '_process_packets' dispatch function as signal
* handler for packet-avail and ready-to-ack signals.
*/
_tx.sigh_packet_avail(_process_packet_dispatcher);
_tx.sigh_ready_to_ack(_process_packet_dispatcher);
}
/**
* Destructor
*/
~Session_component()
{
Dataspace_capability ds = tx_sink()->dataspace();
env()->ram_session()->free(static_cap_cast<Ram_dataspace>(ds));
destroy(&_md_alloc, &_root);
}
/***************************
** File_system interface **
***************************/
File_handle file(Dir_handle dir_handle, Name const &name, Mode mode, bool create)
{
if (!valid_name(name.string()))
throw Invalid_name();
Directory *dir = _handle_registry.lookup_and_lock(dir_handle);
Node_lock_guard dir_guard(*dir);
if (!_writable)
if (create || (mode != STAT_ONLY && mode != READ_ONLY))
throw Permission_denied();
File *file = dir->file(name.string(), mode, create);
Node_lock_guard file_guard(*file);
return _handle_registry.alloc(file);
}
Symlink_handle symlink(Dir_handle dir_handle, Name const &name, bool create)
{
PERR("%s not implemented", __func__);
return Symlink_handle();
}
Dir_handle dir(Path const &path, bool create)
{
char const *path_str = path.string();
_assert_valid_path(path_str);
/* skip leading '/' */
path_str++;
if (!_writable && create)
throw Permission_denied();
if (!path.is_valid_string())
throw Name_too_long();
Directory *dir = _root.subdir(path_str, create);
Node_lock_guard guard(*dir);
return _handle_registry.alloc(dir);
}
Node_handle node(Path const &path)
{
char const *path_str = path.string();
_assert_valid_path(path_str);
Node *node = _root.node(path_str + 1);
Node_lock_guard guard(*node);
return _handle_registry.alloc(node);
}
void close(Node_handle handle)
{
/* FIXME when to destruct node? */
_handle_registry.free(handle);
}
Status status(Node_handle node_handle)
{
Node *node = _handle_registry.lookup_and_lock(node_handle);
Node_lock_guard guard(*node);
Status s;
s.inode = node->inode();
s.size = 0;
s.mode = 0;
File *file = dynamic_cast<File *>(node);
if (file) {
s.size = file->length();
s.mode = File_system::Status::MODE_FILE;
return s;
}
Directory *dir = dynamic_cast<Directory *>(node);
if (dir) {
s.size = dir->num_entries()*sizeof(Directory_entry);
s.mode = File_system::Status::MODE_DIRECTORY;
return s;
}
PERR("%s for symlinks not implemented", __func__);
return Status();
}
void control(Node_handle, Control)
{
PERR("%s not implemented", __func__);
}
void unlink(Dir_handle, Name const &)
{
PERR("%s not implemented", __func__);
}
void truncate(File_handle file_handle, file_size_t size)
{
if (!_writable)
throw Permission_denied();
File *file = _handle_registry.lookup_and_lock(file_handle);
Node_lock_guard file_guard(*file);
file->truncate(size);
}
void move(Dir_handle, Name const &, Dir_handle, Name const &)
{
PERR("%s not implemented", __func__);
}
void sigh(Node_handle node_handle, Signal_context_capability sigh)
{
_handle_registry.sigh(node_handle, sigh);
}
/**
* We could call sync(2) here but for now we forward just the
* reminder because besides testing, there is currently no
* use-case.
*/
void sync() { PWRN("sync() not implemented!"); }
};
class File_system::Root : public Root_component<Session_component>
{
private:
Server::Entrypoint &_ep;
protected:
Session_component *_create_session(const char *args)
{
/*
* Determine client-specific policy defined implicitly by
* the client's label.
*/
char const *root_dir = ".";
bool writeable = false;
enum { ROOT_MAX_LEN = 256 };
char root[ROOT_MAX_LEN];
root[0] = 0;
try {
Session_label label(args);
Session_policy policy(label);
/*
* Determine directory that is used as root directory of
* the session.
*/
try {
policy.attribute("root").value(root, sizeof(root));
/*
* Make sure the root path is specified with a
* leading path delimiter. For performing the
* lookup, we skip the first character.
*/
if (root[0] != '/')
throw Lookup_failed();
root_dir = root + 1;
} catch (Xml_node::Nonexistent_attribute) {
PERR("Missing \"root\" attribute in policy definition");
throw Root::Unavailable();
} catch (Lookup_failed) {
PERR("Session root directory \"%s\" does not exist", root);
throw Root::Unavailable();
}
/*
* Determine if write access is permitted for the session.
*/
try {
writeable = policy.attribute("writeable").has_value("yes");
} catch (Xml_node::Nonexistent_attribute) { }
} catch (Session_policy::No_policy_defined) {
PERR("Invalid session request, no matching policy");
throw Root::Unavailable();
}
size_t ram_quota =
Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
size_t tx_buf_size =
Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
/*
* Check if donated ram quota suffices for session data,
* and communication buffer.
*/
size_t session_size = sizeof(Session_component) + tx_buf_size;
if (max((size_t)4096, session_size) > ram_quota) {
PERR("insufficient 'ram_quota', got %zd, need %zd",
ram_quota, session_size);
throw Root::Quota_exceeded();
}
return new (md_alloc())
Session_component(tx_buf_size, _ep, root_dir, writeable, *md_alloc());
}
public:
/**
* Constructor
*
* \param ep entrypoint
* \param sig_rec signal receiver used for handling the
* data-flow signals of packet streams
* \param md_alloc meta-data allocator
*/
Root(Server::Entrypoint &ep, Allocator &md_alloc)
:
Root_component<Session_component>(&ep.rpc_ep(), &md_alloc),
_ep(ep)
{ }
};
struct File_system::Main
{
Server::Entrypoint &ep;
/*
* Initialize root interface
*/
Sliced_heap sliced_heap = { env()->ram_session(), env()->rm_session() };
Root fs_root = { ep, sliced_heap };
Main(Server::Entrypoint &ep) : ep(ep)
{
env()->parent()->announce(ep.manage(fs_root));
}
};
/**********************
** Server framework **
**********************/
char const * Server::name() { return "lx_fs_ep"; }
Genode::size_t Server::stack_size() { return 2048 * sizeof(long); }
void Server::construct(Server::Entrypoint &ep) { static File_system::Main inst(ep); }

View File

@ -0,0 +1,134 @@
/*
* \brief File-system node
* \author Norman Feske
* \author Christian Helmuth
* \date 2013-11-11
*/
#ifndef _NODE_H_
#define _NODE_H_
/* Genode includes */
#include <util/list.h>
#include <base/lock.h>
#include <base/signal.h>
namespace File_system {
class Listener : public List<Listener>::Element
{
private:
Lock _lock;
Signal_context_capability _sigh;
bool _marked_as_updated;
public:
Listener() : _marked_as_updated(false) { }
Listener(Signal_context_capability sigh)
: _sigh(sigh), _marked_as_updated(false) { }
void notify()
{
Lock::Guard guard(_lock);
if (_marked_as_updated && _sigh.valid())
Signal_transmitter(_sigh).submit();
_marked_as_updated = false;
}
void mark_as_updated()
{
Lock::Guard guard(_lock);
_marked_as_updated = true;
}
bool valid() const { return _sigh.valid(); }
};
class Node : public List<Node>::Element
{
public:
typedef char Name[128];
private:
Lock _lock;
Name _name;
unsigned long const _inode;
List<Listener> _listeners;
public:
Node(unsigned long inode) : _inode(inode) { _name[0] = 0; }
virtual ~Node()
{
/* propagate event to listeners */
mark_as_updated();
notify_listeners();
while (_listeners.first())
_listeners.remove(_listeners.first());
}
unsigned long inode() const { return _inode; }
char const *name() const { return _name; }
/**
* Assign name
*/
void name(char const *name) { strncpy(_name, name, sizeof(_name)); }
void lock() { _lock.lock(); }
void unlock() { _lock.unlock(); }
virtual size_t read(char *dst, size_t len, seek_off_t) = 0;
virtual size_t write(char const *src, size_t len, seek_off_t) = 0;
void add_listener(Listener *listener)
{
_listeners.insert(listener);
}
void remove_listener(Listener *listener)
{
_listeners.remove(listener);
}
void notify_listeners()
{
for (Listener *curr = _listeners.first(); curr; curr = curr->next())
curr->notify();
}
void mark_as_updated()
{
for (Listener *curr = _listeners.first(); curr; curr = curr->next())
curr->mark_as_updated();
}
};
/**
* Guard used for properly releasing node locks
*/
struct Node_lock_guard
{
Node &node;
Node_lock_guard(Node &node) : node(node) { }
~Node_lock_guard() { node.unlock(); }
};
}
#endif /* _NODE_H_ */

View File

@ -0,0 +1,198 @@
/*
* \brief Facility for managing the session-local node-handle namespace
* \author Norman Feske
* \date 2012-04-11
*/
#ifndef _NODE_HANDLE_REGISTRY_H_
#define _NODE_HANDLE_REGISTRY_H_
namespace File_system {
class Node;
class Directory;
class File;
class Symlink;
/**
* Type trait for determining the node type for a given handle type
*/
template<typename T> struct Node_type;
template<> struct Node_type<Node_handle> { typedef Node Type; };
template<> struct Node_type<Dir_handle> { typedef Directory Type; };
template<> struct Node_type<File_handle> { typedef File Type; };
template<> struct Node_type<Symlink_handle> { typedef Symlink Type; };
/**
* Type trait for determining the handle type for a given node type
*/
template<typename T> struct Handle_type;
template<> struct Handle_type<Node> { typedef Node_handle Type; };
template<> struct Handle_type<Directory> { typedef Dir_handle Type; };
template<> struct Handle_type<File> { typedef File_handle Type; };
template<> struct Handle_type<Symlink> { typedef Symlink_handle Type; };
class Node_handle_registry
{
private:
/* maximum number of open nodes per session */
enum { MAX_NODE_HANDLES = 128U };
Lock mutable _lock;
Node *_nodes[MAX_NODE_HANDLES];
/**
* Each open node handle can act as a listener to be informed about
* node changes.
*/
Listener _listeners[MAX_NODE_HANDLES];
/**
* Allocate node handle
*
* \throw Out_of_node_handles
*/
int _alloc(Node *node)
{
Lock::Guard guard(_lock);
for (unsigned i = 0; i < MAX_NODE_HANDLES; i++)
if (!_nodes[i]) {
_nodes[i] = node;
return i;
}
throw Out_of_node_handles();
}
bool _in_range(int handle) const
{
return ((handle >= 0) && (handle < MAX_NODE_HANDLES));
}
public:
Node_handle_registry()
{
for (unsigned i = 0; i < MAX_NODE_HANDLES; i++)
_nodes[i] = 0;
}
template <typename NODE_TYPE>
typename Handle_type<NODE_TYPE>::Type alloc(NODE_TYPE *node)
{
typedef typename Handle_type<NODE_TYPE>::Type Handle;
return Handle(_alloc(node));
}
/**
* Release node handle
*/
void free(Node_handle handle)
{
Lock::Guard guard(_lock);
if (!_in_range(handle.value))
return;
/*
* Notify listeners about the changed file.
*/
Node *node = dynamic_cast<Node *>(_nodes[handle.value]);
if (!node) { return; }
node->lock();
node->notify_listeners();
/*
* De-allocate handle
*/
Listener &listener = _listeners[handle.value];
if (listener.valid())
node->remove_listener(&listener);
_nodes[handle.value] = 0;
listener = Listener();
node->unlock();
}
/**
* Lookup node using its handle as key
*
* The node returned by this function is in a locked state.
*
* \throw Invalid_handle
*/
template <typename HANDLE_TYPE>
typename Node_type<HANDLE_TYPE>::Type *lookup_and_lock(HANDLE_TYPE handle)
{
Lock::Guard guard(_lock);
if (!_in_range(handle.value))
throw Invalid_handle();
typedef typename Node_type<HANDLE_TYPE>::Type Node;
Node *node = dynamic_cast<Node *>(_nodes[handle.value]);
if (!node)
throw Invalid_handle();
node->lock();
return node;
}
bool refer_to_same_node(Node_handle h1, Node_handle h2) const
{
Lock::Guard guard(_lock);
if (!_in_range(h1.value) || !_in_range(h2.value)) {
PDBG("refer_to_same_node -> Invalid_handle");
throw Invalid_handle();
}
return _nodes[h1.value] == _nodes[h2.value];
}
/**
* Register signal handler to be notified of node changes
*/
void sigh(Node_handle handle, Signal_context_capability sigh)
{
Lock::Guard guard(_lock);
if (!_in_range(handle.value))
throw Invalid_handle();
Node *node = dynamic_cast<Node *>(_nodes[handle.value]);
if (!node) {
PDBG("Invalid_handle");
throw Invalid_handle();
}
node->lock();
Node_lock_guard node_lock_guard(*node);
Listener &listener = _listeners[handle.value];
/*
* If there was already a handler registered for the node,
* remove the old handler.
*/
if (listener.valid())
node->remove_listener(&listener);
/*
* Register new handler
*/
listener = Listener(sigh);
node->add_listener(&listener);
}
};
}
#endif /* _NODE_HANDLE_REGISTRY_H_ */

View File

@ -0,0 +1,50 @@
/*
* \brief Symlink file-system node
* \author Norman Feske
* \author Christian Helmuth
* \date 2013-11-11
*
* FIXME unfinished
*/
#ifndef _SYMLINK_H_
#define _SYMLINK_H_
/* local includes */
#include <node.h>
#include <lx_util.h>
namespace File_system {
class Symlink;
}
class File_system::Symlink : public Node
{
private:
char _link_to[MAX_PATH_LEN];
public:
Symlink(char const *name) { Node::name(name); }
size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
size_t count = min(len, sizeof(_link_to) + 1);
Genode::strncpy(dst, _link_to, count);
return count;
}
size_t write(char const *src, size_t len, seek_off_t seek_offset)
{
size_t count = min(len, sizeof(_link_to) + 1);
Genode::strncpy(_link_to, src, count);
return count;
}
file_size_t length() const { return strlen(_link_to) + 1; }
};
#endif /* _SYMLINK_H_ */

View File

@ -0,0 +1,6 @@
TARGET = lx_fs
REQUIRES = linux
SRC_CC = main.cc
LIBS = base config server lx_hybrid
INC_DIR += $(PRG_DIR) /usr/include

View File

@ -0,0 +1,63 @@
/*
* \brief Utilities
* \author Norman Feske
* \date 2012-04-11
*/
#ifndef _UTIL_H_
#define _UTIL_H_
/**
* Return base-name portion of null-terminated path string
*/
static inline char const *basename(char const *path)
{
char const *start = path;
for (; *path; path++)
if (*path == '/')
start = path + 1;
return start;
}
/**
* Return true if specified path is a base name (contains no path delimiters)
*/
static inline bool is_basename(char const *path)
{
for (; *path; path++)
if (*path == '/')
return false;
return true;
}
/**
* Return true if character 'c' occurs in null-terminated string 'str'
*/
static inline bool string_contains(char const *str, char c)
{
for (; *str; str++)
if (*str == c)
return true;
return false;
}
/**
* Return true if 'str' is a valid node name
*/
static inline bool valid_name(char const *str)
{
if (string_contains(str, '/')) return false;
/* must have at least one character */
if (str[0] == 0) return false;
return true;
}
#endif /* _UTIL_H_ */

View File

@ -0,0 +1,415 @@
/*
* \brief Audio_out Mixer
* \author Sebastian Sumpf
* \date 2012-12-20
*
* The mixer impelements the audio session on the server side. For each channel
* (currently 'left' and 'right' only) it supports multiple client sessions and
* mixes its input into a single client audio session.
*/
/*
* Copyright (C) 2009-2013 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 <base/lock.h>
#include <base/env.h>
#include <base/sleep.h>
#include <audio_out_session/rpc_object.h>
#include <audio_out_session/connection.h>
#include <cap_session/connection.h>
#include <root/component.h>
#include <timer_session/connection.h>
using namespace Genode;
static bool verbose = false;
enum Channel_number { LEFT, RIGHT, MAX_CHANNELS };
namespace Audio_out
{
class Session_elem;
class Session_component;
class Root;
class Channel;
class Mixer;
}
static Audio_out::Channel *_channels[MAX_CHANNELS];
static int channel_number_from_string(const char *name)
{
static struct Names {
const char *name;
Channel_number number;
} names[] = {
{ "left", LEFT }, { "front left", LEFT },
{ "right", RIGHT }, { "front right", RIGHT },
{ 0, MAX_CHANNELS }
};
for (Names *n = names; n->name; ++n)
if (!strcmp(name, n->name))
return n->number;
return -1;
}
/**
* Makes audio a list element
*/
struct Audio_out::Session_elem : Audio_out::Session_rpc_object,
List<Audio_out::Session_elem>::Element
{
Session_elem(Signal_context_capability data_cap)
: Session_rpc_object(data_cap) { }
};
/**
* One channel containing multiple sessions
*/
class Audio_out::Channel
{
private:
List<Session_elem> _list;
public:
void insert(Session_elem *session) {
_list.insert(session); }
Session_elem *first() {
return _list.first();
}
};
/**
* The mixer
*/
class Audio_out::Mixer : public Thread<1024 * sizeof(addr_t)>
{
private:
Lock _sleep_lock;
Connection _left; /* left output */
Connection _right; /* right output */
Connection *_out[MAX_CHANNELS];
Signal_receiver *_data_recv; /* data availble signal receiver
(send from clients) */
void _sleep() { _sleep_lock.lock(); }
Mixer()
:
Thread("audio_out_mixer"),
_sleep_lock(Lock::LOCKED), _left("left", false, true),
_right("right", false, true)
{
_out[LEFT] = &_left;
_out[RIGHT] = &_right;
start();
}
/* check for active sessions */
bool _check_active()
{
bool active = false;
for (int i = 0; i < MAX_CHANNELS; i++) {
Session_elem *session = _channels[i]->first();
while (session) {
active |= session->active();
session = session->next();
}
}
return active;
}
void _advance_session(Session_elem *session, unsigned pos)
{
if (session->stopped())
return;
Stream *stream = session->stream();
bool full = stream->full();
/* mark packets as played and icrement position pointer */
while (stream->pos() != pos) {
stream->get(stream->pos())->mark_as_played();
stream->increment_position();
}
/* send 'progress' sginal */
session->progress_submit();
/* send 'alloc' signal */
if (full)
session->alloc_submit();
}
/* advance 'positions' of all client sessions */
void _advance_position()
{
for (int i = 0; i < MAX_CHANNELS; i++) {
Session_elem *session = _channels[i]->first();
unsigned pos = _out[i]->stream()->pos();
while (session) {
_advance_session(session, pos);
session = session->next();
}
}
}
/* mix one packet */
void _mix_packet(Packet *out, Packet *in, bool clear)
{
/* when clear is set, set input an zero out remainder */
if (clear) {
out->content(in->content(), PERIOD);
} else {
/* mix */
for (int i = 0; i < PERIOD; i++) {
out->content()[i] += in->content()[i];
if (out->content()[i] > 1) out->content()[i] = 1;
if (out->content()[i] < -1) out->content()[i] = -1;
}
}
/* mark packet as processed */
in->invalidate();
}
/* get packet at offset */
Packet *session_packet(Session *session, unsigned offset)
{
Stream *s = session->stream();
return s->get(s->pos() + offset);
}
/* mix all session of one channel */
bool _mix_channel(Channel_number nr, unsigned out_pos, unsigned offset)
{
Stream *out_stream = _out[nr]->stream();
Packet *out = out_stream->get(out_pos + offset);
bool clear = true;
bool mix_all = false;
bool out_valid = out->valid();
for (Session_elem *session = _channels[nr]->first();
session;
session = session->next()) {
if (session->stopped())
continue;
Packet *in = session_packet(session, offset);
/*
* When there already is an out packet, start over and mix
* everything.
*/
if (in->valid() && out_valid && !mix_all) {
clear = true;
mix_all = true;
session = _channels[nr]->first();
in = session_packet(session, offset);
}
/* skip if packet has been processed or was already played */
if ((!in->valid() && !mix_all) || in->played())
continue;
_mix_packet(out, in, clear);
if (verbose)
PDBG("mix: ch %u in %u -> out %u all %d o: %u",
nr, session->stream()->packet_position(in),
out_stream->packet_position(out), mix_all, offset);
clear = false;
}
return !clear;
}
void _mix()
{
unsigned pos[MAX_CHANNELS];
pos[LEFT] = _out[LEFT]->stream()->pos();
pos[RIGHT] = _out[RIGHT]->stream()->pos();
/*
* Look for packets that are valid, mix channels in an alternating
* way.
*/
for (int i = 0; i < QUEUE_SIZE; i++) {
bool mix_one = true;
for (int j = LEFT; j < MAX_CHANNELS; j++)
mix_one &= _mix_channel((Channel_number)j, pos[j], i);
if (mix_one) {
_out[LEFT]->submit(_out[LEFT]->stream()->get(pos[LEFT] + i));
_out[RIGHT]->submit(_out[RIGHT]->stream()->get(pos[RIGHT] + i));
}
}
}
#define FOREACH_CHANNEL(func) ({ \
for (int i = 0; i < MAX_CHANNELS; i++) \
_out[i]->func(); \
})
void _wait_for_progress() { FOREACH_CHANNEL(wait_for_progress); }
void _start() { FOREACH_CHANNEL(start); }
void _stop() { FOREACH_CHANNEL(stop); }
public:
void entry()
{
_start();
while (true) {
if (!_check_active()) {
_stop();
_sleep();
_start();
continue;
}
_mix();
/* advance position of clients */
_advance_position();
if (!_left.stream()->empty())
_wait_for_progress();
else
_data_recv->wait_for_signal();
}
}
void wakeup() { _sleep_lock.unlock(); }
/* sync client position with output position */
void sync_pos(Channel_number channel, Stream *stream) {
stream->pos(_out[channel]->stream()->pos()); }
void data_recv(Signal_receiver *recv) { _data_recv = recv; }
static Mixer *m()
{
static Mixer _m;
return &_m;
}
};
class Audio_out::Session_component : public Audio_out::Session_elem
{
private:
Channel_number _channel;
public:
Session_component(Channel_number channel,
Signal_context_capability data_cap)
: Session_elem(data_cap), _channel(channel) { }
void start()
{
Session_rpc_object::start();
/* sync audio position with mixer */
Mixer::m()->sync_pos(_channel, stream());
Mixer::m()->wakeup();
}
};
namespace Audio_out {
typedef Root_component<Session_component, Multiple_clients> Root_component;
}
class Audio_out::Root : public Audio_out::Root_component
{
Signal_context_capability _data_cap;
Session_component *_create_session(const char *args)
{
char channel_name[16];
Arg_string::find_arg(args, "channel").string(channel_name,
sizeof(channel_name),
"left");
size_t ram_quota =
Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
size_t session_size = align_addr(sizeof(Session_component), 12);
if ((ram_quota < session_size) ||
(sizeof(Stream) > ram_quota - session_size)) {
PERR("insufficient 'ram_quota', got %zd, need %zd",
ram_quota, sizeof(Stream) + session_size);
throw Root::Quota_exceeded();
}
int ch = channel_number_from_string(channel_name);
if (ch < 0)
throw Root::Invalid_args();
Session_component *session = new (md_alloc())
Session_component((Channel_number)ch, _data_cap);
PDBG("Added new \"%s\" nr: %u s: %p",channel_name, ch, session);
_channels[ch]->insert(session);
return session;
}
public:
Root(Rpc_entrypoint *session_ep,
Signal_context_capability data_cap,
Allocator *md_alloc)
: Root_component(session_ep, md_alloc), _data_cap(data_cap) { }
};
int main()
{
static Cap_connection cap;
enum { STACK_SIZE = 4*1024*sizeof(addr_t) };
for (int i = 0; i < MAX_CHANNELS; i++)
_channels[i] = new (env()->heap()) Audio_out::Channel();
static Signal_receiver data_recv;
static Signal_context data_context;
static Signal_context_capability data_cap(data_recv.manage(&data_context));
/* start mixer thread */
Audio_out::Mixer::m()->data_recv(&data_recv);
static Rpc_entrypoint ep(&cap, STACK_SIZE, "audio_ep");
static Audio_out::Root root(&ep, data_cap, env()->heap());
env()->parent()->announce(ep.manage(&root));
sleep_forever();
return 0;
}

View File

@ -0,0 +1,3 @@
TARGET = mixer
SRC_CC = mixer.cc
LIBS = base

View File

@ -0,0 +1,43 @@
The NIC bridge provides multiple sessions of the 'Nic' service while using a
single 'Nic' session for forwarding requests. It implements a flavour of the
Proxy-ARP protocol (rfc1027). That means it allocates a virtual MAC address for
each client. Whenever a client sends a packet, NIC bridge changes the sender's
MAC address to the one it memorized for the client. Moreover, it monitors DHCP
packets, and tracks the IP addresses assigned to each of its clients. Whenever
ARP packets come from the outside, NIC bridge will answer them with the
corresponding MAC address.
By adding a 'mac' attribute to the 'nic_bridge' config node: one can define the
first MAC address from which the NIC bridge will allocate MACs for its clients.
For example:
! <config mac="02:02:02:02:02:00"/>
Note that the least relevant byte will be ignored. NIC bridge will use it for
enumerating its clients, starting from 0.
Normally, NIC bridge is expected to be used in scenarios where an DHCP server
is available. However, there are situations where the use of static IPs for
virtual NICs is useful. For example, when using the NIC bridge to create a
virtual network between the lighttpd web server and the Arora web browser, both
running as Genode processes without real network connectivity.
The static IP can be configured per client of the NIC bridge using a '<policy>'
node of the configuration. For example, the following policy assigns a static
address to a client with the session label "lighttpd".
!<start name="nic_bridge">
! ...
! <config>
! <policy label="lighttpd" ip_addr="10.0.2.55"/>
! </config>
!</start>
Of course, the client needs to configure its TCP/IP stack to use the assigned
IP address. This can be done via configuration arguments examined by the
'lwip_nic_dhcp' libc plugin. For the given example, the configuration for the
lighttpd process would look as follows.
!<start name="lighttpd">
! <config>
! <interface ip_addr="10.0.2.55"
! netmask="255.255.255.0"
! gateway="10.0.2.1"/>
! </config>
!</start>

View File

@ -0,0 +1,103 @@
/*
* \brief Address-node holds a client-specific session-component.
* \author Stefan Kalkowski
* \date 2010-08-25
*/
/*
* Copyright (C) 2010-2013 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 _ADDRESS_NODE_H_
#define _ADDRESS_NODE_H_
/* Genode */
#include <util/avl_tree.h>
#include <util/list.h>
#include <nic_session/nic_session.h>
#include <net/netaddress.h>
#include <net/ethernet.h>
#include <net/ipv4.h>
namespace Net {
/* Forward declaration */
class Session_component;
/**
* An Address_node encapsulates a session-component and can be hold in
* a list and/or avl-tree, whereby the network-address (MAC or IP)
* acts as a key.
*/
template <unsigned LEN>
class Address_node : public Genode::Avl_node<Address_node<LEN> >,
public Genode::List<Address_node<LEN> >::Element
{
public:
typedef Network_address<LEN> Address;
private:
Address _addr; /* MAC or IP address */
Session_component *_component; /* client's component */
public:
/**
* Constructor
*
* \param addr Network address acting as sorting criteria.
* \param component pointer to client's session component.
*/
Address_node(Address addr, Session_component *component)
: _addr(addr), _component(component) { }
/***************
** Accessors **
***************/
Address addr() { return _addr; }
Session_component *component() { return _component; }
/************************
** Avl node interface **
************************/
bool higher(Address_node *c)
{
using namespace Genode;
return (memcmp(&c->_addr.addr, &_addr.addr,
sizeof(_addr.addr)) > 0);
}
/**
* Find by address
*/
Address_node *find_by_address(Address addr)
{
using namespace Genode;
if (addr == _addr)
return this;
bool side = memcmp(&addr.addr, _addr.addr,
sizeof(_addr.addr)) > 0;
Address_node *c = Avl_node<Address_node>::child(side);
return c ? c->find_by_address(addr) : 0;
}
};
typedef Address_node<Ipv4_packet::ADDR_LEN> Ipv4_address_node;
typedef Address_node<Ethernet_frame::ADDR_LEN> Mac_address_node;
}
#endif /* _ADDRESS_NODE_H_ */

View File

@ -0,0 +1,47 @@
/*
* \brief Thread-safe Avl_tree implementation
* \author Stefan Kalkowski
* \date 2010-08-20
*/
/*
* Copyright (C) 2010-2013 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 _AVL_SAFE_H_
#define _AVL_SAFE_H_
/* Genode */
#include <base/lock.h>
#include <base/lock_guard.h>
#include <util/avl_tree.h>
/**
* Lock-guarded avl-tree implementation.
*/
template <typename NT>
class Avl_tree_safe : public Genode::Avl_tree<NT>
{
private:
Genode::Lock _lock;
public:
void insert(Genode::Avl_node<NT> *node)
{
Genode::Lock::Guard lock_guard(_lock);
Genode::Avl_tree<NT>::insert(node);
}
void remove(Genode::Avl_node<NT> *node)
{
Genode::Lock::Guard lock_guard(_lock);
Genode::Avl_tree<NT>::remove(node);
}
};
#endif /* _AVL_SAFE_H_ */

View File

@ -0,0 +1,160 @@
/*
* \brief Proxy-ARP session and root component
* \author Stefan Kalkowski
* \date 2010-08-18
*/
/*
* Copyright (C) 2010-2013 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 */
#include <net/arp.h>
#include <net/dhcp.h>
#include <net/udp.h>
#include "env.h"
#include "component.h"
#include "nic.h"
#include "vlan.h"
using namespace Net;
static const int verbose = 1;
bool Session_component::handle_arp(Ethernet_frame *eth, Genode::size_t size)
{
Arp_packet *arp =
new (eth->data()) Arp_packet(size - sizeof(Ethernet_frame));
if (arp->ethernet_ipv4() &&
arp->opcode() == Arp_packet::REQUEST) {
/*
* 'Gratuitous ARP' broadcast messages are used to announce newly created
* IP<->MAC address mappings to other hosts. nic_bridge-internal hosts
* would expect a nic_bridge-internal MAC address in this message, whereas
* external hosts would expect the NIC's MAC address in this message.
* The simplest solution to this problem is to just drop those messages,
* since they are not really necessary.
*/
if (arp->src_ip() == arp->dst_ip())
return false;
Ipv4_address_node *node = Env::vlan()->ip_tree()->first();
if (node)
node = node->find_by_address(arp->dst_ip());
if (!node) {
arp->src_mac(Net::Env::nic()->mac());
}
}
return true;
}
bool Session_component::handle_ip(Ethernet_frame *eth, Genode::size_t size)
{
Ipv4_packet *ip =
new (eth->data()) Ipv4_packet(size - sizeof(Ethernet_frame));
if (ip->protocol() == Udp_packet::IP_ID)
{
Udp_packet *udp = new (ip->data())
Udp_packet(size - sizeof(Ipv4_packet));
if (Dhcp_packet::is_dhcp(udp)) {
Dhcp_packet *dhcp = new (udp->data())
Dhcp_packet(size - sizeof(Ipv4_packet) - sizeof(Udp_packet));
if (dhcp->op() == Dhcp_packet::REQUEST) {
dhcp->broadcast(true);
udp->calc_checksum(ip->src(), ip->dst());
}
}
}
return true;
}
void Session_component::finalize_packet(Ethernet_frame *eth,
Genode::size_t size)
{
Mac_address_node *node = Env::vlan()->mac_tree()->first();
if (node)
node = node->find_by_address(eth->dst());
if (node)
node->component()->send(eth, size);
else {
/* set our MAC as sender */
eth->src(Net::Env::nic()->mac());
Net::Env::nic()->send(eth, size);
}
}
void Session_component::_free_ipv4_node()
{
if (_ipv4_node) {
Env::vlan()->ip_tree()->remove(_ipv4_node);
destroy(this->guarded_allocator(), _ipv4_node);
}
}
void Session_component::set_ipv4_address(Ipv4_packet::Ipv4_address ip_addr)
{
_free_ipv4_node();
_ipv4_node = new (this->guarded_allocator())
Ipv4_address_node(ip_addr, this);
Net::Env::vlan()->ip_tree()->insert(_ipv4_node);
}
Session_component::Session_component(Genode::Allocator *allocator,
Genode::size_t amount,
Genode::size_t tx_buf_size,
Genode::size_t rx_buf_size,
Ethernet_frame::Mac_address vmac,
Genode::Rpc_entrypoint &ep,
char *ip_addr)
: Guarded_range_allocator(allocator, amount),
Tx_rx_communication_buffers(tx_buf_size, rx_buf_size),
Session_rpc_object(Tx_rx_communication_buffers::tx_ds(),
Tx_rx_communication_buffers::rx_ds(),
this->range_allocator(), ep),
_mac_node(vmac, this),
_ipv4_node(0)
{
Env::vlan()->mac_tree()->insert(&_mac_node);
Env::vlan()->mac_list()->insert(&_mac_node);
/* static ip parsing */
if (ip_addr != 0 && Genode::strlen(ip_addr)) {
Ipv4_packet::Ipv4_address ip = Ipv4_packet::ip_from_string(ip_addr);
if (ip == Ipv4_packet::Ipv4_address()) {
PWRN("Empty or error ip address. Skipped.");
} else {
set_ipv4_address(ip);
if (verbose)
PLOG("vmac=%02x:%02x:%02x:%02x:%02x:%02x ip=%d.%d.%d.%d",
vmac.addr[0], vmac.addr[1], vmac.addr[2],
vmac.addr[3], vmac.addr[4], vmac.addr[5],
ip.addr[0], ip.addr[1], ip.addr[2], ip.addr[3]);
}
}
_tx.sigh_ready_to_ack(_sink_ack);
_tx.sigh_packet_avail(_sink_submit);
_rx.sigh_ack_avail(_source_ack);
_rx.sigh_ready_to_submit(_source_submit);
}
Session_component::~Session_component() {
Env::vlan()->mac_tree()->remove(&_mac_node);
Env::vlan()->mac_list()->remove(&_mac_node);
_free_ipv4_node();
}

View File

@ -0,0 +1,242 @@
/*
* \brief Proxy-ARP session and root component
* \author Stefan Kalkowski
* \date 2010-08-18
*/
/*
* Copyright (C) 2010-2013 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 _COMPONENT_H_
#define _COMPONENT_H_
/* Genode */
#include <base/lock.h>
#include <root/component.h>
#include <util/arg_string.h>
#include <nic/packet_allocator.h>
#include <nic_session/rpc_object.h>
#include <nic_session/connection.h>
#include <net/ipv4.h>
#include <base/allocator_guard.h>
#include <os/session_policy.h>
#include "address_node.h"
#include "mac.h"
#include "packet_handler.h"
namespace Net {
/**
* Helper class.
*
*/
class Guarded_range_allocator
{
private:
Genode::Allocator_guard _guarded_alloc;
::Nic::Packet_allocator _range_alloc;
public:
Guarded_range_allocator(Genode::Allocator *backing_store,
Genode::size_t amount)
: _guarded_alloc(backing_store, amount),
_range_alloc(&_guarded_alloc) {}
Genode::Allocator_guard *guarded_allocator() {
return &_guarded_alloc; }
Genode::Range_allocator *range_allocator() {
return static_cast<Genode::Range_allocator *>(&_range_alloc); }
};
class Communication_buffer : Genode::Ram_dataspace_capability
{
public:
Communication_buffer(Genode::size_t size)
: Genode::Ram_dataspace_capability(Genode::env()->ram_session()->alloc(size))
{ }
~Communication_buffer() { Genode::env()->ram_session()->free(*this); }
Genode::Dataspace_capability dataspace() { return *this; }
};
class Tx_rx_communication_buffers
{
private:
Communication_buffer _tx_buf, _rx_buf;
public:
Tx_rx_communication_buffers(Genode::size_t tx_size,
Genode::size_t rx_size)
: _tx_buf(tx_size), _rx_buf(rx_size) { }
Genode::Dataspace_capability tx_ds() { return _tx_buf.dataspace(); }
Genode::Dataspace_capability rx_ds() { return _rx_buf.dataspace(); }
};
/**
* Nic-session component class
*
* We must inherit here from Guarded_range_allocator, although aggregation
* would be more convinient, because the range-allocator needs to be initialized
* before base-class Session_rpc_object.
*/
class Session_component : public Guarded_range_allocator,
private Tx_rx_communication_buffers,
public ::Nic::Session_rpc_object,
public Packet_handler
{
private:
Mac_address_node _mac_node;
Ipv4_address_node *_ipv4_node;
void _free_ipv4_node();
public:
/**
* Constructor
*
* \param allocator backing store for guarded allocator
* \param amount amount of memory managed by guarded allocator
* \param tx_buf_size buffer size for tx channel
* \param rx_buf_size buffer size for rx channel
* \param vmac virtual mac address
* \param ep entry point used for packet stream
*/
Session_component(Genode::Allocator *allocator,
Genode::size_t amount,
Genode::size_t tx_buf_size,
Genode::size_t rx_buf_size,
Ethernet_frame::Mac_address vmac,
Genode::Rpc_entrypoint &ep,
char *ip_addr = 0);
~Session_component();
::Nic::Mac_address mac_address()
{
::Nic::Mac_address m;
Mac_address_node::Address mac = _mac_node.addr();
Genode::memcpy(&m, mac.addr, sizeof(m.addr));
return m;
}
void set_ipv4_address(Ipv4_packet::Ipv4_address ip_addr);
/******************************
** Packet_handler interface **
******************************/
Packet_stream_sink< ::Nic::Session::Policy> * sink() {
return _tx.sink(); }
Packet_stream_source< ::Nic::Session::Policy> * source() {
return _rx.source(); }
bool handle_arp(Ethernet_frame *eth, Genode::size_t size);
bool handle_ip(Ethernet_frame *eth, Genode::size_t size);
void finalize_packet(Ethernet_frame *eth, Genode::size_t size);
};
/*
* Root component, handling new session requests.
*/
class Root : public Genode::Root_component<Session_component>
{
private:
enum { verbose = 1 };
Mac_allocator _mac_alloc;
Genode::Rpc_entrypoint &_ep;
protected:
enum { MAX_IP_ADDR_LENGTH = 16, };
char ip_addr[MAX_IP_ADDR_LENGTH];
Session_component *_create_session(const char *args)
{
using namespace Genode;
memset(ip_addr, 0, MAX_IP_ADDR_LENGTH);
try {
Session_label label(args);
Session_policy policy(label);
policy.attribute("ip_addr").value(ip_addr, sizeof(ip_addr));
if (verbose) PLOG("policy: %s ip_addr = %s", label.string(), ip_addr);
} catch (Xml_node::Nonexistent_attribute) {
if (verbose) PLOG("Missing \"ip_addr\" attribute in policy definition");
} catch (Session_policy::No_policy_defined) {
if (verbose) PLOG("Invalid session request, no matching policy");;
}
size_t ram_quota =
Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
size_t tx_buf_size =
Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
size_t rx_buf_size =
Arg_string::find_arg(args, "rx_buf_size").ulong_value(0);
/* delete ram quota by the memory needed for the session */
size_t session_size = max((size_t)4096, sizeof(Session_component));
if (ram_quota < session_size)
throw Root::Quota_exceeded();
/*
* Check if donated ram quota suffices for both
* communication buffers. Also check both sizes separately
* to handle a possible overflow of the sum of both sizes.
*/
if (tx_buf_size > ram_quota - session_size
|| rx_buf_size > ram_quota - session_size
|| tx_buf_size + rx_buf_size > ram_quota - session_size) {
PERR("insufficient 'ram_quota', got %zd, need %zd",
ram_quota, tx_buf_size + rx_buf_size + session_size);
throw Root::Quota_exceeded();
}
try {
return new (md_alloc()) Session_component(env()->heap(),
ram_quota - session_size,
tx_buf_size,
rx_buf_size,
_mac_alloc.alloc(),
_ep,
ip_addr);
} catch(Mac_allocator::Alloc_failed) {
PWRN("Mac address allocation failed!");
return (Session_component*) 0;
}
}
public:
Root(Genode::Rpc_entrypoint *session_ep,
Genode::Allocator *md_alloc)
: Genode::Root_component<Session_component>(session_ep, md_alloc),
_ep(*session_ep) { }
};
} /* namespace Net */
#endif /* _COMPONENT_H_ */

View File

@ -0,0 +1,36 @@
/*
* \brief Nic-bridge global environment
* \author Stefan Kalkowski
* \date 2013-05-23
*/
/*
* Copyright (C) 2013 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 "env.h"
#include "nic.h"
#include "vlan.h"
Genode::Signal_receiver* Net::Env::receiver()
{
static Genode::Signal_receiver receiver;
return &receiver;
}
Net::Vlan* Net::Env::vlan()
{
static Net::Vlan vlan;
return &vlan;
}
Net::Nic* Net::Env::nic()
{
static Net::Nic nic;
return &nic;
}

View File

@ -0,0 +1,36 @@
/*
* \brief Proxy-ARP environment
* \author Stefan Kalkowski
* \date 2013-05-24
*
* A database containing all clients sorted by IP and MAC addresses.
*/
/*
* Copyright (C) 2013 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 _SRC__SERVER__NIC_BRIDGE__ENV_H_
#define _SRC__SERVER__NIC_BRIDGE__ENV_H_
#include <base/signal.h>
namespace Net {
class Vlan;
class Nic;
class Env;
}
struct Net::Env
{
static Genode::Signal_receiver* receiver();
static Vlan *vlan();
static Net::Nic *nic();
};
#endif /* _SRC__SERVER__NIC_BRIDGE__ENV_H_ */

View File

@ -0,0 +1,45 @@
/*
* \brief Thread-safe list implementation
* \author Stefan Kalkowski
* \date 2010-08-23
*/
/*
* Copyright (C) 2010-2013 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 _LIST_SAFE_H_
#define _LIST_SAFE_H_
#include <base/lock_guard.h>
#include <util/list.h>
/**
* Lock-guarded avl-tree implementation.
*/
template <typename LE>
class List_safe : public Genode::List<LE>
{
private:
Genode::Lock _lock;
public:
void insert(LE *item)
{
Genode::Lock::Guard lock_guard(_lock);
Genode::List<LE>::insert(item);
}
void remove(LE *item)
{
Genode::Lock::Guard lock_guard(_lock);
Genode::List<LE>::remove(item);
}
};
#endif /* _LIST_SAFE_H_ */

View File

@ -0,0 +1,21 @@
/*
* \brief MAC-address allocator
* \author Stefan Kalkowski
* \date 2010-08-25
*/
/*
* Copyright (C) 2010-2013 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 "mac.h"
/**
* We take the range 02:02:02:02:02:XX for our MAC address allocator,
* it's likely, that we will have no clashes here.
* (e.g. Linux uses 02:00... for its tap-devices.)
*/
Net::Ethernet_frame::Mac_address Net::Mac_allocator::mac_addr_base(0x02);

View File

@ -0,0 +1,80 @@
/*
* \brief MAC-address allocator
* \author Stefan Kalkowski
* \date 2010-08-25
*/
/*
* Copyright (C) 2010-2013 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 _MAC_H_
#define _MAC_H_
/* Genode */
#include <base/exception.h>
#include <net/ethernet.h>
namespace Net {
/**
* The MAC allocator is used to administer MAC addresses for
* the proxy-ARP's client's.
*/
class Mac_allocator
{
private:
/* limit available MAC addresses to one byte range */
enum { MSB_MAX = 0xFF };
/* signals, whether most significant byte is in use */
typedef struct
{
unsigned used : 1;
} Msb;
Msb _msbs[MSB_MAX]; /* bitfield of MSBs */
public:
class Alloc_failed : Genode::Exception {};
/* reference MAC address */
static Ethernet_frame::Mac_address mac_addr_base;
Mac_allocator() { Genode::memset(&_msbs, 0, sizeof(_msbs)); }
/**
* Allocates a new MAC address.
*
* \throws Alloc_failed if no more MAC addresses are available.
* \return MAC address
*/
Ethernet_frame::Mac_address alloc()
{
for (int i=0; i < MSB_MAX; i++) {
if (!_msbs[i].used) {
_msbs[i].used = 1;
Ethernet_frame::Mac_address mac = mac_addr_base;
mac.addr[5] = i;
return mac;
}
}
throw Alloc_failed();
}
/**
* Frees a formerly allocated MAC address.
*/
void free(Ethernet_frame::Mac_address mac) {
_msbs[(unsigned)mac.addr[5]].used = 0; }
};
}
#endif /* _MAC_H_ */

View File

@ -0,0 +1,64 @@
/*
* \brief Proxy-ARP for Nic-session
* \author Stefan Kalkowski
* \date 2010-08-18
*/
/*
* Copyright (C) 2010-2013 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 */
#include <base/env.h>
#include <cap_session/connection.h>
#include <nic_session/connection.h>
#include <nic/packet_allocator.h>
#include <nic/xml_node.h>
#include <os/config.h>
/* local includes */
#include "component.h"
#include "nic.h"
#include "env.h"
int main(int, char **)
{
using namespace Genode;
/* read MAC address prefix from config file */
try {
Nic::Mac_address mac;
Genode::config()->xml_node().attribute("mac").value(&mac);
Genode::memcpy(&Net::Mac_allocator::mac_addr_base, &mac,
sizeof(Net::Mac_allocator::mac_addr_base));
} catch(...) {}
try {
enum { STACK_SIZE = 2048*sizeof(Genode::addr_t) };
static Cap_connection cap;
static Rpc_entrypoint ep(&cap, STACK_SIZE, "nic_bridge_ep");
static Net::Root nic_root(&ep, env()->heap());
/* announce NIC service */
env()->parent()->announce(ep.manage(&nic_root));
/* connect to NIC backend to actually see incoming traffic */
Net::Ethernet_frame::Mac_address mac(Net::Env::nic()->mac());
printf("--- NIC bridge started (mac=%02x:%02x:%02x:%02x:%02x:%02x) ---\n",
mac.addr[0], mac.addr[1], mac.addr[2],
mac.addr[3], mac.addr[4], mac.addr[5]);
while (true) {
Signal s = Net::Env::receiver()->wait_for_signal();
static_cast<Signal_dispatcher_base *>(s.context())->dispatch(s.num());
}
} catch (Parent::Service_denied) {
PERR("Could not connect to uplink NIC");
}
return 0;
}

View File

@ -0,0 +1,137 @@
/*
* \brief NIC handler
* \author Stefan Kalkowski
* \date 2013-05-24
*/
/*
* Copyright (C) 2013 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 <base/env.h>
#include <net/ethernet.h>
#include <net/arp.h>
#include <net/ipv4.h>
#include <net/udp.h>
#include <net/dhcp.h>
#include "address_node.h"
#include "component.h"
#include "env.h"
#include "nic.h"
#include "vlan.h"
using namespace Net;
bool Net::Nic::handle_arp(Ethernet_frame *eth, Genode::size_t size) {
Arp_packet *arp = new (eth->data())
Arp_packet(size - sizeof(Ethernet_frame));
/* ignore broken packets */
if (!arp->ethernet_ipv4())
return true;
/* look whether the IP address is one of our client's */
Ipv4_address_node *node = Env::vlan()->ip_tree()->first();
if (node)
node = node->find_by_address(arp->dst_ip());
if (node) {
if (arp->opcode() == Arp_packet::REQUEST) {
/*
* The ARP packet gets re-written, we interchange source
* and destination MAC and IP addresses, and set the opcode
* to reply, and then push the packet back to the NIC driver.
*/
Ipv4_packet::Ipv4_address old_src_ip = arp->src_ip();
arp->opcode(Arp_packet::REPLY);
arp->dst_mac(arp->src_mac());
arp->src_mac(mac());
arp->src_ip(arp->dst_ip());
arp->dst_ip(old_src_ip);
eth->dst(arp->dst_mac());
/* set our MAC as sender */
eth->src(mac());
send(eth, size);
} else {
/* overwrite destination MAC */
arp->dst_mac(node->component()->mac_address().addr);
eth->dst(node->component()->mac_address().addr);
node->component()->send(eth, size);
}
return false;
}
return true;
}
bool Net::Nic::handle_ip(Ethernet_frame *eth, Genode::size_t size) {
Ipv4_packet *ip = new (eth->data())
Ipv4_packet(size - sizeof(Ethernet_frame));
/* is it an UDP packet ? */
if (ip->protocol() == Udp_packet::IP_ID)
{
Udp_packet *udp = new (ip->data())
Udp_packet(size - sizeof(Ipv4_packet));
/* is it a DHCP packet ? */
if (Dhcp_packet::is_dhcp(udp)) {
Dhcp_packet *dhcp = new (udp->data())
Dhcp_packet(size - sizeof(Ipv4_packet) - sizeof(Udp_packet));
/* check for DHCP ACKs containing new client ips */
if (dhcp->op() == Dhcp_packet::REPLY) {
Dhcp_packet::Option *ext = dhcp->option(Dhcp_packet::MSG_TYPE);
if (ext) {
/*
* extract the IP address and set it in the
* client's session-component
*/
Genode::uint8_t *msg_type = (Genode::uint8_t*) ext->value();
if (*msg_type == Dhcp_packet::DHCP_ACK) {
Mac_address_node *node =
Env::vlan()->mac_tree()->first();
if (node)
node = node->find_by_address(dhcp->client_mac());
if (node)
node->component()->set_ipv4_address(dhcp->yiaddr());
}
}
}
}
}
/* is it an unicast message to one of our clients ? */
if (eth->dst() == Net::Env::nic()->mac()) {
Ipv4_address_node *node = Env::vlan()->ip_tree()->first();
if (node) {
node = node->find_by_address(ip->dst());
if (node) {
/* overwrite destination MAC */
eth->dst(node->component()->mac_address().addr);
/* deliver the packet to the client */
node->component()->send(eth, size);
return false;
}
}
}
return true;
}
Net::Nic::Nic()
: _tx_block_alloc(Genode::env()->heap()),
_nic(&_tx_block_alloc, BUF_SIZE, BUF_SIZE),
_mac(_nic.mac_address().addr)
{
_nic.rx_channel()->sigh_ready_to_ack(_sink_ack);
_nic.rx_channel()->sigh_packet_avail(_sink_submit);
_nic.tx_channel()->sigh_ack_avail(_source_ack);
_nic.tx_channel()->sigh_ready_to_submit(_source_submit);
}

View File

@ -0,0 +1,64 @@
/*
* \brief Proxy-ARP NIC session handler
* \author Stefan Kalkowski
* \date 2010-08-18
*/
/*
* Copyright (C) 2010-2013 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 _SRC__SERVER__NIC_BRIDGE__NIC_H_
#define _SRC__SERVER__NIC_BRIDGE__NIC_H_
#include <nic_session/connection.h>
#include <nic/packet_allocator.h>
#include "env.h"
#include "packet_handler.h"
namespace Net {
class Nic;
}
class Net::Nic : public Net::Packet_handler
{
private:
enum {
PACKET_SIZE = ::Nic::Packet_allocator::DEFAULT_PACKET_SIZE,
BUF_SIZE = ::Nic::Session::QUEUE_SIZE * PACKET_SIZE,
};
::Nic::Packet_allocator _tx_block_alloc;
::Nic::Connection _nic;
Ethernet_frame::Mac_address _mac;
public:
Nic();
::Nic::Connection *nic() { return &_nic; }
Ethernet_frame::Mac_address mac() { return _mac; }
/******************************
** Packet_handler interface **
******************************/
Packet_stream_sink< ::Nic::Session::Policy> * sink() {
return _nic.rx(); }
Packet_stream_source< ::Nic::Session::Policy> * source() {
return _nic.tx(); }
bool handle_arp(Ethernet_frame *eth, Genode::size_t size);
bool handle_ip(Ethernet_frame *eth, Genode::size_t size);
void finalize_packet(Ethernet_frame *eth, Genode::size_t size) {}
};
#endif /* _SRC__SERVER__NIC_BRIDGE__NIC_H_ */

View File

@ -0,0 +1,125 @@
/*
* \brief Packet handler handling network packets.
* \author Stefan Kalkowski
* \date 2010-08-18
*/
/*
* Copyright (C) 2010-2013 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 <base/lock.h>
#include <net/arp.h>
#include <net/dhcp.h>
#include <net/ethernet.h>
#include <net/ipv4.h>
#include <net/udp.h>
#include "env.h"
#include "component.h"
#include "packet_handler.h"
#include "vlan.h"
using namespace Net;
static const bool verbose = true;
void Packet_handler::_ready_to_submit(unsigned)
{
/* as long as packets are available, and we can ack them */
while (sink()->packet_avail()) {
_packet = sink()->get_packet();
if (!_packet.valid()) continue;
handle_ethernet(sink()->packet_content(_packet), _packet.size());
if (!sink()->ready_to_ack()) {
if (verbose)
PWRN("ack state FULL");
return;
}
sink()->acknowledge_packet(_packet);
}
}
void Packet_handler::_ready_to_ack(unsigned)
{
/* check for acknowledgements */
while (source()->ack_avail())
source()->release_packet(source()->get_acked_packet());
}
void Packet_handler::broadcast_to_clients(Ethernet_frame *eth, Genode::size_t size)
{
/* check whether it's really a broadcast packet */
if (eth->dst() == Ethernet_frame::BROADCAST) {
/* iterate through the list of clients */
Mac_address_node *node =
Env::vlan()->mac_list()->first();
while (node) {
/* deliver packet */
node->component()->send(eth, size);
node = node->next();
}
}
}
void Packet_handler::handle_ethernet(void* src, Genode::size_t size)
{
try {
/* parse ethernet frame header */
Ethernet_frame *eth = new (src) Ethernet_frame(size);
switch (eth->type()) {
case Ethernet_frame::ARP:
if (!handle_arp(eth, size)) return;
break;
case Ethernet_frame::IPV4:
if(!handle_ip(eth, size)) return;
break;
default:
;
}
broadcast_to_clients(eth, size);
finalize_packet(eth, size);
} catch(Arp_packet::No_arp_packet) {
PWRN("Invalid ARP packet!");
} catch(Ethernet_frame::No_ethernet_frame) {
PWRN("Invalid ethernet frame");
} catch(Dhcp_packet::No_dhcp_packet) {
PWRN("Invalid IPv4 packet!");
} catch(Ipv4_packet::No_ip_packet) {
PWRN("Invalid IPv4 packet!");
} catch(Udp_packet::No_udp_packet) {
PWRN("Invalid UDP packet!");
}
}
void Packet_handler::send(Ethernet_frame *eth, Genode::size_t size)
{
try {
/* copy and submit packet */
Packet_descriptor packet = source()->alloc_packet(size);
char *content = source()->packet_content(packet);
Genode::memcpy((void*)content, (void*)eth, size);
source()->submit_packet(packet);
} catch(Packet_stream_source< ::Nic::Session::Policy>::Packet_alloc_failed) {
if (verbose)
PWRN("Packet dropped");
}
}
Packet_handler::Packet_handler()
: _sink_ack(*Net::Env::receiver(), *this, &Packet_handler::_ack_avail),
_sink_submit(*Net::Env::receiver(), *this, &Packet_handler::_ready_to_submit),
_source_ack(*Net::Env::receiver(), *this, &Packet_handler::_ready_to_ack),
_source_submit(*Net::Env::receiver(), *this, &Packet_handler::_packet_avail)
{ }

View File

@ -0,0 +1,132 @@
/*
* \brief Signal driven NIC packet handler
* \author Stefan Kalkowski
* \date 2010-08-18
*/
/*
* Copyright (C) 2010-2013 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 _PACKET_HANDLER_H_
#define _PACKET_HANDLER_H_
/* Genode */
#include <base/semaphore.h>
#include <base/thread.h>
#include <nic_session/connection.h>
#include <net/ethernet.h>
#include <net/ipv4.h>
namespace Net {
class Packet_handler;
}
/**
* Generic packet handler used as base for NIC and client packet handlers.
*/
class Net::Packet_handler
{
private:
Packet_descriptor _packet;
/**
* submit queue not empty anymore
*/
void _ready_to_submit(unsigned);
/**
* acknoledgement queue not full anymore
*
* TODO: by now, we assume ACK and SUBMIT queue to be equally
* dimensioned. That's why we ignore this signal by now.
*/
void _ack_avail(unsigned) { }
/**
* acknoledgement queue not empty anymore
*/
void _ready_to_ack(unsigned);
/**
* submit queue not full anymore
*
* TODO: by now, we just drop packets that cannot be transferred
* to the other side, that's why we ignore this signal.
*/
void _packet_avail(unsigned) { }
protected:
Genode::Signal_dispatcher<Packet_handler> _sink_ack;
Genode::Signal_dispatcher<Packet_handler> _sink_submit;
Genode::Signal_dispatcher<Packet_handler> _source_ack;
Genode::Signal_dispatcher<Packet_handler> _source_submit;
public:
Packet_handler();
virtual Packet_stream_sink< ::Nic::Session::Policy> * sink() = 0;
virtual Packet_stream_source< ::Nic::Session::Policy> * source() = 0;
/**
* Broadcasts ethernet frame to all clients,
* as long as its really a broadcast packtet.
*
* \param eth ethernet frame to send.
* \param size ethernet frame's size.
*/
void inline broadcast_to_clients(Ethernet_frame *eth,
Genode::size_t size);
/**
* Send ethernet frame
*
* \param eth ethernet frame to send.
* \param size ethernet frame's size.
*/
void send(Ethernet_frame *eth, Genode::size_t size);
/**
* Handle an ethernet packet
*
* \param src ethernet frame's address
* \param size ethernet frame's size.
*/
void handle_ethernet(void* src, Genode::size_t size);
/*
* Handle an ARP packet
*
* \param eth ethernet frame containing the ARP packet.
* \param size ethernet frame's size.
*/
virtual bool handle_arp(Ethernet_frame *eth,
Genode::size_t size) = 0;
/*
* Handle an IP packet
*
* \param eth ethernet frame containing the IP packet.
* \param size ethernet frame's size.
*/
virtual bool handle_ip(Ethernet_frame *eth,
Genode::size_t size) = 0;
/*
* Finalize handling of ethernet frame.
*
* \param eth ethernet frame to handle.
* \param size ethernet frame's size.
*/
virtual void finalize_packet(Ethernet_frame *eth,
Genode::size_t size) = 0;
};
#endif /* _PACKET_HANDLER_H_ */

View File

@ -0,0 +1,5 @@
TARGET = nic_bridge
LIBS = base net config
SRC_CC = component.cc env.cc mac.cc main.cc nic.cc packet_handler.cc
vpath *.cc $(REP_DIR)/src/server/proxy_arp

View File

@ -0,0 +1,53 @@
/*
* \brief Virtual local network.
* \author Stefan Kalkowski
* \date 2010-08-18
*
* A database containing all clients sorted by IP and MAC addresses.
*/
/*
* Copyright (C) 2010-2013 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 _VLAN_H_
#define _VLAN_H_
#include "address_node.h"
#include "avl_safe.h"
#include "list_safe.h"
namespace Net {
/*
* The Vlan is a database containing all clients
* sorted by IP and MAC addresses.
*/
class Vlan
{
public:
typedef Avl_tree_safe<Mac_address_node> Mac_address_tree;
typedef Avl_tree_safe<Ipv4_address_node> Ipv4_address_tree;
typedef List_safe<Mac_address_node> Mac_address_list;
private:
Mac_address_tree _mac_tree;
Mac_address_list _mac_list;
Ipv4_address_tree _ip_tree;
public:
Vlan() {}
Mac_address_tree *mac_tree() { return &_mac_tree; }
Mac_address_list *mac_list() { return &_mac_list; }
Ipv4_address_tree *ip_tree() { return &_ip_tree; }
};
}
#endif /* _VLAN_H_ */

View File

@ -0,0 +1,211 @@
/*
* \brief Simple loop-back pseudo network adaptor
* \author Norman Feske
* \date 2009-11-13
*
* This program showcases the server-side use of the 'Nic_session' interface.
*/
/*
* Copyright (C) 2009-2013 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 <base/env.h>
#include <base/sleep.h>
#include <cap_session/connection.h>
#include <root/component.h>
#include <util/arg_string.h>
#include <util/misc_math.h>
#include <nic_session/rpc_object.h>
#include <nic_session/client.h>
#include <timer_session/connection.h>
namespace Nic {
class Communication_buffer : Genode::Ram_dataspace_capability
{
public:
Communication_buffer(Genode::size_t size)
: Genode::Ram_dataspace_capability(Genode::env()->ram_session()->alloc(size))
{ }
~Communication_buffer() { Genode::env()->ram_session()->free(*this); }
Genode::Dataspace_capability dataspace() { return *this; }
};
class Tx_rx_communication_buffers
{
private:
Communication_buffer _tx_buf, _rx_buf;
public:
Tx_rx_communication_buffers(Genode::size_t tx_size,
Genode::size_t rx_size)
: _tx_buf(tx_size), _rx_buf(rx_size) { }
Genode::Dataspace_capability tx_ds() { return _tx_buf.dataspace(); }
Genode::Dataspace_capability rx_ds() { return _rx_buf.dataspace(); }
};
enum { STACK_SIZE = 8*1024 };
class Session_component : private Genode::Allocator_avl,
private Tx_rx_communication_buffers,
private Genode::Thread<STACK_SIZE>,
public Session_rpc_object
{
private:
/**
* Packet-handling thread
*/
void entry()
{
using namespace Genode;
for (;;) {
Packet_descriptor packet_from_client, packet_to_client;
/* get packet, block until a packet is available */
packet_from_client = _tx.sink()->get_packet();
if (!packet_from_client.valid()) {
PWRN("received invalid packet");
continue;
}
size_t packet_size = packet_from_client.size();
try {
packet_to_client = _rx.source()->alloc_packet(packet_size);
Genode::memcpy(_rx.source()->packet_content(packet_to_client),
_tx.sink()->packet_content(packet_from_client),
packet_size);
_rx.source()->submit_packet(packet_to_client);
} catch (Session::Rx::Source::Packet_alloc_failed) {
PWRN("transmit packet allocation failed, drop packet");
}
if (!_tx.sink()->ready_to_ack())
printf("need to wait until ready-for-ack\n");
_tx.sink()->acknowledge_packet(packet_from_client);
/* flush acknowledgements for the echoes packets */
while (_rx.source()->ack_avail())
_rx.source()->release_packet(_rx.source()->get_acked_packet());
}
}
public:
/**
* Constructor
*
* \param tx_buf_size buffer size for tx channel
* \param rx_buf_size buffer size for rx channel
* \param rx_block_md_alloc backing store of the meta data of the
* rx block allocator
* \param ep entry point used for packet stream
* channels
*/
Session_component(Genode::size_t tx_buf_size,
Genode::size_t rx_buf_size,
Genode::Allocator *rx_block_md_alloc,
Genode::Rpc_entrypoint &ep)
:
Genode::Allocator_avl(rx_block_md_alloc),
Tx_rx_communication_buffers(tx_buf_size, rx_buf_size),
Thread("nic_packet_handler"),
Session_rpc_object(Tx_rx_communication_buffers::tx_ds(),
Tx_rx_communication_buffers::rx_ds(),
static_cast<Genode::Range_allocator *>(this), ep)
{
/* start packet-handling thread */
start();
}
Mac_address mac_address()
{
Mac_address result = {{1,2,3,4,5,6}};
return result;
}
};
class Root : public Genode::Root_component<Session_component>
{
private:
Genode::Rpc_entrypoint &_channel_ep;
protected:
Session_component *_create_session(const char *args)
{
using namespace Genode;
size_t ram_quota = Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
size_t tx_buf_size = Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
size_t rx_buf_size = Arg_string::find_arg(args, "rx_buf_size").ulong_value(0);
/* deplete ram quota by the memory needed for the session structure */
size_t session_size = max(4096UL, (unsigned long)sizeof(Session_component));
if (ram_quota < session_size)
throw Root::Quota_exceeded();
/*
* Check if donated ram quota suffices for both communication
* buffers. Also check both sizes separately to handle a
* possible overflow of the sum of both sizes.
*/
if (tx_buf_size > ram_quota - session_size
|| rx_buf_size > ram_quota - session_size
|| tx_buf_size + rx_buf_size > ram_quota - session_size) {
PERR("insufficient 'ram_quota', got %zd, need %zd",
ram_quota, tx_buf_size + rx_buf_size + session_size);
throw Root::Quota_exceeded();
}
return new (md_alloc()) Session_component(tx_buf_size, rx_buf_size,
env()->heap(), _channel_ep);
}
public:
Root(Genode::Rpc_entrypoint *session_ep,
Genode::Allocator *md_alloc)
:
Genode::Root_component<Session_component>(session_ep, md_alloc),
_channel_ep(*session_ep)
{ }
};
}
using namespace Genode;
int main(int, char **)
{
enum { STACK_SIZE = 2*4096 };
static Cap_connection cap;
static Rpc_entrypoint ep(&cap, STACK_SIZE, "nicloop_ep");
static Nic::Root nic_root(&ep, env()->heap());
env()->parent()->announce(ep.manage(&nic_root));
sleep_forever();
return 0;
}

View File

@ -0,0 +1,3 @@
TARGET = nic_loopback
SRC_CC = main.cc
LIBS = base

View File

@ -0,0 +1,11 @@
The 'nit_fb' server provides a single session of the 'Input' and 'Framebuffer'
services by presenting a Nitpicker view at a predefined screen position. If
instantiated multiple times, it can be used to statically arrange multiple
virtual frame buffers on one physical screen. The size and screen position
of each 'nit_fb' instance can be defined via Genode's configuration mechansim
using the following attributes of the 'nit_fb' config node:
! <config xpos="100" ypos="150" width="300" height="200" refresh_rate="25"/>
If 'refresh_rate' isn't set the server will not trigger any refresh operations
by itself.

View File

@ -0,0 +1,249 @@
/*
* \brief Framebuffer-to-Nitpicker adapter
* \author Norman Feske
* \date 2010-09-09
*/
/*
* Copyright (C) 2010-2013 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/sleep.h>
#include <nitpicker_view/client.h>
#include <cap_session/connection.h>
#include <nitpicker_session/connection.h>
#include <dataspace/client.h>
#include <input_session/input_session.h>
#include <input/event.h>
#include <os/config.h>
#include <os/static_root.h>
#include <timer_session/connection.h>
namespace Input {
/**
* Input session applying a position offset to absolute motion events
*/
class Session_component : public Genode::Rpc_object<Session>
{
private:
/**
* Offset to be applied to absolute motion events
*/
int _dx, _dy;
/**
* Input session, from which we fetch events
*/
Input::Session *_from_input;
Genode::Dataspace_capability _from_input_ds;
Genode::size_t _from_ev_buf_size;
Input::Event *_from_ev_buf;
/**
* Input session, to which to provide events
*/
Genode::Dataspace_capability _to_input_ds;
Input::Event *_to_ev_buf;
/**
* Shortcut for mapping an event buffer locally
*/
Input::Event *_map_ev_buf(Genode::Dataspace_capability ds_cap) {
return Genode::env()->rm_session()->attach(ds_cap); }
public:
/**
* Constructor
*
* \param dx, dy offset to be added to absolute motion events
* \param from_input input session from where to get input events
*/
Session_component(int dx, int dy, Input::Session *from_input)
:
_dx(dx), _dy(dy),
_from_input(from_input),
_from_input_ds(from_input->dataspace()),
_from_ev_buf_size(Genode::Dataspace_client(_from_input_ds).size()),
_from_ev_buf(_map_ev_buf(_from_input_ds)),
_to_input_ds(Genode::env()->ram_session()->alloc(_from_ev_buf_size)),
_to_ev_buf(_map_ev_buf(_to_input_ds))
{ }
/*****************************
** Input session interface **
*****************************/
Genode::Dataspace_capability dataspace() { return _to_input_ds; }
bool is_pending() const { return _from_input->is_pending(); }
int flush()
{
/* flush events at input session */
int num_events = _from_input->flush();
/* copy events from input buffer to client buffer */
for (int i = 0; i < num_events; i++) {
Input::Event e = _from_ev_buf[i];
/* apply view offset to absolute motion events */
if (e.is_absolute_motion())
e = Event(e.type(), e.code(),
e.ax() + _dx, e.ay() + _dy, 0, 0);
_to_ev_buf[i] = e;
}
return num_events;
}
};
}
/**
* Read integer value from config attribute
*/
long config_arg(const char *attr, long default_value)
{
long res = default_value;
try { Genode::config()->xml_node().attribute(attr).value(&res); }
catch (...) { }
return res;
}
struct Config_args
{
long xpos, ypos, width, height;
unsigned refresh_rate;
Config_args()
:
xpos(config_arg("xpos", 0)),
ypos(config_arg("ypos", 0)),
width(config_arg("width", 0)),
height(config_arg("height", 0)),
refresh_rate(config_arg("refresh_rate", 0))
{ }
};
int main(int argc, char **argv)
{
using namespace Genode;
/*
* Read arguments from config
*/
Config_args cfg;
long view_x = cfg.xpos, view_y = cfg.ypos,
view_w = cfg.width, view_h = cfg.height;
/*
* Open Nitpicker session
*/
static Nitpicker::Connection nitpicker;
/*
* If no config was provided, use screen size of Nitpicker
*/
if (view_w == 0 || view_h == 0) {
Framebuffer::Mode const mode = nitpicker.mode();
view_w = mode.width(), view_h = mode.height();
}
/*
* Setup virtual framebuffer
*/
Framebuffer::Mode const mode(view_w, view_h, Framebuffer::Mode::RGB565);
nitpicker.buffer(mode, false);
PINF("using xywh=(%ld,%ld,%ld,%ld) refresh_rate=%u",
view_x, view_y, view_w, view_h, cfg.refresh_rate);
/*
* Create Nitpicker view and bring it to front
*/
Nitpicker::View_client view(nitpicker.create_view());
view.viewport(view_x, view_y, view_w, view_h, 0, 0, false);
view.stack(Nitpicker::View_capability(), true, true);
/*
* Initialize server entry point
*/
enum { STACK_SIZE = 4096 };
static Cap_connection cap;
static Rpc_entrypoint ep(&cap, STACK_SIZE, "nitfb_ep");
/*
* Let the entry point serve the framebuffer and input root interfaces
*/
static Static_root<Framebuffer::Session> fb_root(nitpicker.framebuffer_session());
/*
* Pre-initialize single client input session
*/
static Input::Session_client nit_input(nitpicker.input_session());
static Input::Session_component input_session(-view_x, -view_y, &nit_input);
/*
* Attach input root interface to the entry point
*/
static Static_root<Input::Session> input_root(ep.manage(&input_session));
/*
* Announce services
*/
env()->parent()->announce(ep.manage(&fb_root));
env()->parent()->announce(ep.manage(&input_root));
/*
* Register signal handler for config changes
*/
Signal_receiver sig_rec;
Signal_context sig_ctx;
config()->sigh(sig_rec.manage(&sig_ctx));
for (;;) {
bool reload_config = false;
if (!cfg.refresh_rate) {
sig_rec.wait_for_signal();
reload_config = true;
} else {
static Timer::Connection timer;
static Framebuffer::Session_client nit_fb(nitpicker.framebuffer_session());
timer.msleep(cfg.refresh_rate);
nit_fb.refresh(0, 0, view_w, view_h);
if (sig_rec.pending()) {
sig_rec.wait_for_signal();
reload_config = true;
}
}
if (reload_config) {
try {
config()->reload();
cfg = Config_args();
view.viewport(cfg.xpos, cfg.ypos, cfg.width, cfg.height, 0, 0, true);
} catch (...) {
PERR("Error while reloading config");
}
}
}
return 0;
}

View File

@ -0,0 +1,3 @@
TARGET = nit_fb
SRC_CC = main.cc
LIBS = base config

View File

@ -0,0 +1,74 @@
Nitpicker is the low-level GUI server of Genode. For the general concepts,
please refer to the following document:
:A Nitpickers guide to a minimal-complexity secure GUI:
[http://genode-labs.com/publications/nitpicker-secure-gui-2005.pdf]
Configuration
~~~~~~~~~~~~~
Nitpicker supports the following configuration options, supplied via
Genode's config mechanism.
Tinting of clients in X-Ray mode
--------------------------------
Nitpicker allows for assigning a color to single clients or a groups
of clients based on the client's label. The following configuration
tints all views of the launchpad subsystem in blue except for those
views that belong to the testnit child of launchpad.
! <config>
! <policy label="launchpad" color="#0000ff"/>
! <policy label="launchpad -> testnit" color="#ff0000"/>
! </config>
Global key definitions
----------------------
Nitpicker has a few built-in function that can be activated via global
keyboard shortcuts, namely the X-ray mode and the kill mode. The keys
for toggling those functions can be defined as follows:
! <config>
! <global-keys>
! <key name="KEY_SCROLLLOCK" operation="xray" />
! <key name="KEY_PRINT" operation="kill" />
! </global-keys>
! </config>
The '<global-keys>' node contains the policy for handling global keys. Each
'<key>' subnode expresses a rule for named key. The 'operation' attribute
refers nitpicker's built-in operations. In the example above, the X-ray
mode can be activated via the scroll-lock key and the kill mode can be
activated via the print key.
Alternatively to specifying an 'operation' attribute, a key node can contain
a 'label' attribute. If specified, all events regarding the key will be
reported to the client with the specified label. This enables clients to
handle global shortcuts. The client with the matching label will receive
all events until the number of concurrently pressed keys reaches zero.
This way, it is possible to handle chords of multiple keys starting with
the key specified in the '<key>' node. For the routing of global keys to
clients, the order of '<key>' nodes is important. If multiple nodes exists for
different labels, the first match will take effect. For example:
! <global-keys>
! <key name="KEY_F11" label="launchpad -> testnit" />
! <key name="KEY_F11" label="launchpad" />
! </global-keys>
The "launchpad" client will receive all key sequences starting with F11 unless
the "launchpad -> testnit" program is running. As soon as testnit gets started
by launchpad, testnit will receive the events. If the order was reversed,
launchpad would always receive the events.
Background color
----------------
The background color can be defined via the '<background>' config node:
! <background color="#112233" />

View File

@ -0,0 +1,63 @@
/*
* \brief Nitpicker background
* \author Norman Feske
* \date 2006-06-22
*/
/*
* Copyright (C) 2006-2013 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 _BACKGROUND_H_
#define _BACKGROUND_H_
#include <nitpicker_gfx/box_painter.h>
#include "view.h"
#include "clip_guard.h"
struct Background : private Texture_base, Session, View
{
Color color;
/*
* The background uses no texture. Therefore
* we can pass a null pointer as texture argument
* to the Session constructor.
*/
Background(Area size)
:
Texture_base(Area(0, 0)), Session(Genode::Session_label(""), 0, false),
View(*this, View::NOT_STAY_TOP, View::NOT_TRANSPARENT, View::BACKGROUND, 0),
color(25, 37, 50)
{
View::geometry(Rect(Point(0, 0), size));
}
/***********************
** Session interface **
***********************/
void submit_input_event(Input::Event) { }
/********************
** View interface **
********************/
int frame_size(Mode const &mode) const { return 0; }
void frame(Canvas_base &canvas, Mode const &mode) { }
void draw(Canvas_base &canvas, Mode const &mode) const
{
Rect const view_rect = abs_geometry();
Clip_guard clip_guard(canvas, view_rect);
canvas.draw_box(view_rect, color);
}
};
#endif /* _BACKGROUND_H_ */

View File

@ -0,0 +1,36 @@
/*
* \brief Mouse cursor pixel data
* \author Norman Feske
* \date 2006-08-09
*/
/*
* Copyright (C) 2006-2013 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.
*/
static struct {
unsigned short w, h, pixels[16][16];
} big_mouse = {
16,16,
{
{0x738E,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},
{0x0000,0x94B2,0x7BCF,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},
{0x0000,0x630C,0xC638,0xC638,0x6B4D,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},
{0x0000,0x0000,0x738E,0x8C71,0xFFFF,0xB5B6,0x6B4D,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},
{0x0000,0x0000,0x630C,0x4A49,0x630C,0xB5B6,0xFFFF,0xB5B6,0x630C,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},
{0x0000,0x0000,0x0000,0x528A,0x528A,0x630C,0x9492,0xB5B6,0xFFFF,0xB5B6,0x630C,0x0000,0x0000,0x0000,0x0000,0x0000},
{0x0000,0x0000,0x0000,0x528A,0x39C7,0x4208,0x630C,0x7BCF,0xB5B6,0xFFFF,0xFFFF,0xB5B6,0x630C,0x0000,0x0000,0x0000},
{0x0000,0x0000,0x0000,0x0000,0x4208,0x39C7,0x4208,0x630C,0x7BCF,0xB5B6,0xDEFB,0xFFFF,0xFFFF,0xB5B6,0x630C,0x0000},
{0x0000,0x0000,0x0000,0x0000,0x4A49,0x1082,0x39C7,0x4208,0x5ACB,0x7BCF,0x8C71,0xAD75,0x630C,0x0000,0x0000,0x0000},
{0x0000,0x0000,0x0000,0x0000,0x0000,0x4208,0x1082,0x39C7,0x5ACB,0x630C,0xB5B6,0x0000,0x0000,0x0000,0x0000,0x0000},
{0x0000,0x0000,0x0000,0x0000,0x0000,0x4A49,0x1082,0x1082,0x39C7,0x4A49,0x630C,0xAD75,0x0000,0x0000,0x0000,0x0000},
{0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x4208,0x1082,0x4A49,0x0000,0x4A49,0x630C,0x8C71,0x0000,0x0000,0x0000},
{0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x4A49,0x39C7,0x4A49,0x0000,0x0000,0x4A49,0x630C,0x8C71,0x0000,0x0000},
{0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x4A49,0x0000,0x0000,0x0000,0x0000,0x4A49,0x630C,0x8C71,0x0000},
{0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x4A49,0x0000,0x0000,0x0000,0x0000,0x0000,0x4A49,0x4A49,0x0000},
{0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000},
}
};

View File

@ -0,0 +1,102 @@
/*
* \brief Graphics backend used by nitpicker
* \author Norman Feske
* \date 2013-12-29
*/
/*
* Copyright (C) 2013 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 _CANVAS_H_
#define _CANVAS_H_
#include <nitpicker_gfx/box_painter.h>
#include <nitpicker_gfx/text_painter.h>
#include <nitpicker_gfx/texture_painter.h>
typedef Genode::Surface_base::Area Area;
typedef Genode::Surface_base::Point Point;
typedef Genode::Surface_base::Rect Rect;
typedef Genode::Color Color;
using Genode::Texture_base;
using Genode::Texture;
/**
* Pixel-type-independent interface of nitpicker's graphics backend
*/
struct Canvas_base
{
virtual Area size() const = 0;
virtual Rect clip() const = 0;
virtual void clip(Rect) = 0;
virtual void flush_pixels(Rect) = 0;
virtual void draw_box(Rect, Color) = 0;
virtual void draw_texture(Point, Texture_base const &, Texture_painter::Mode,
Color mix_color, bool allow_alpha) = 0;
virtual void draw_text(Point, Text_painter::Font const &, Color,
char const *string) = 0;
};
/**
* Pixel-type-specific implementation of nitpicker's graphics backend
*/
template <typename PT>
class Canvas : public Canvas_base, public Genode::Surface_base::Flusher
{
private:
Genode::Surface<PT> _surface;
public:
Canvas(PT *base, Area size) : _surface(base, size)
{
_surface.flusher(this);
}
/**
* Default implementation of Surface_base::Flusher interface
*/
void flush_pixels(Rect) { }
Area size() const { return _surface.size(); }
Rect clip() const { return _surface.clip(); }
void clip(Rect rect) { _surface.clip(rect); }
void draw_box(Rect rect, Color color)
{
Box_painter::paint(_surface, rect, color);
}
void draw_texture(Point pos, Texture_base const &texture_base,
Texture_painter::Mode mode, Color mix_color,
bool allow_alpha)
{
Texture<PT> const &texture = static_cast<Texture<PT> const &>(texture_base);
Texture_painter::paint(_surface, texture, mix_color, pos, mode,
allow_alpha);
}
void draw_text(Point pos, Text_painter::Font const &font,
Color color, char const *string)
{
Text_painter::paint(_surface, pos, font, color, string);
}
};
#endif /* _CANVAS_H_ */

View File

@ -0,0 +1,110 @@
/*
* \brief Chunky-pixel-based menubar
* \author Norman Feske
* \date 2006-08-22
*/
/*
* Copyright (C) 2006-2013 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 _CHUNKY_MENUBAR_
#define _CHUNKY_MENUBAR_
#include <nitpicker_gfx/box_painter.h>
#include <nitpicker_gfx/texture_painter.h>
#include "menubar.h"
template <typename PT>
class Chunky_menubar : public Texture<PT>,
public Session,
public Menubar,
public View
{
private:
Canvas<PT> _canvas;
public:
Chunky_menubar(PT *pixels, Area size)
:
Texture<PT>(pixels, 0, size),
Session(Genode::Session_label(""), 0, false),
View(*this, View::STAY_TOP, View::NOT_TRANSPARENT,
View::NOT_BACKGROUND, 0),
_canvas(pixels, size)
{
View::geometry(Rect(Point(0, 0), size));
Session::texture(this, false);
}
/***********************
** Session interface **
***********************/
void submit_input_event(Input::Event) { }
/********************
** View interface **
********************/
int frame_size(Mode const &mode) const { return 0; }
void frame(Canvas_base &canvas, Mode const &mode) { }
void draw(Canvas_base &canvas, Mode const &mode)
{
Clip_guard clip_guard(canvas, *this);
/* draw menubar content */
canvas.draw_texture(abs_position(), *this, Texture_painter::SOLID, BLACK, false);
}
/***********************
** Menubar interface **
***********************/
void state(Menubar_state const state)
{
*static_cast<Menubar_state *>(this) = state;
/* choose base color dependent on the Nitpicker state */
int r = (mode.kill()) ? 200 : (mode.xray()) ? session_color.r : (session_color.r + 100) >> 1;
int g = (mode.kill()) ? 70 : (mode.xray()) ? session_color.g : (session_color.g + 100) >> 1;
int b = (mode.kill()) ? 70 : (mode.xray()) ? session_color.b : (session_color.b + 100) >> 1;
Rect const view_rect = abs_geometry();
/* highlight first line with slightly brighter color */
_canvas.draw_box(Rect(Point(0, 0), Area(view_rect.w(), 1)),
Color(r + (r / 2), g + (g / 2), b + (b / 2)));
/* draw slightly shaded background */
for (unsigned i = 1; i < view_rect.h() - 1; i++) {
r -= r > 3 ? 4 : 0;
g -= g > 3 ? 4 : 0;
b -= b > 4 ? 4 : 0;
_canvas.draw_box(Rect(Point(0, i), Area(view_rect.w(), 1)), Color(r, g, b));
}
/* draw last line darker */
_canvas.draw_box(Rect(Point(0, view_rect.h() - 1), Area(view_rect.w(), 1)),
Color(r / 4, g / 4, b / 4));
/* draw label */
draw_label(_canvas, view_rect.center(label_size(session_label.string(),
view_title.string())), session_label.string(),
WHITE, view_title.string(), session_color);
}
using Menubar::state;
};
#endif

View File

@ -0,0 +1,54 @@
/*
* \brief Clipping guard
* \author Norman Feske
* \date 2006-08-08
*
* When drawing views recursively, we need to successively shrink the clipping
* area to the intersection of the existing clipping area and the area of the
* current view. After each drawing operation, we want to restore the previous
* clipping area. The clipping guard functions the same way as a lock guard.
* The clipping area of the canvas that is specified at the constructor of the
* clipping guard gets shrinked as long as the clip guard exists. When we leave
* the drawing function, all local variables including the clipping guard gets
* destroyed and the clipping guard's destructor resets the clipping area of
* the canvas.
*
* This mechanism effectively replaces the explicit clipping stack of the
* original Nitpicker version by folding the clipping stack into the normal
* stack.
*/
/*
* Copyright (C) 2006-2013 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 _CLIP_GUARD_H_
#define _CLIP_GUARD_H_
#include "canvas.h"
class Clip_guard
{
private:
Canvas_base &_canvas;
Rect const _orig_clip_rect;
public:
Clip_guard(Canvas_base &canvas, Rect new_clip_rect)
:
_canvas(canvas),
_orig_clip_rect(_canvas.clip())
{
_canvas.clip(Rect::intersect(_orig_clip_rect, new_clip_rect));
}
~Clip_guard() { _canvas.clip(_orig_clip_rect); }
};
#endif /* _CLIP_GUARD_H_ */

View File

@ -0,0 +1,27 @@
/*
* \brief Color definitions used by nitpicker
* \date 2006-08-04
* \author Norman Feske
*/
/*
* Copyright (C) 2006-2013 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 _COLOR_H_
#define _COLOR_H_
#include <util/color.h>
/*
* Symbolic names for some important colors
*/
static const Genode::Color BLACK(0, 0, 0);
static const Genode::Color WHITE(255, 255, 255);
static const Genode::Color FRAME_COLOR(255, 200, 127);
static const Genode::Color KILL_COLOR(255, 0, 0);
#endif /* _COLOR_H_ */

Binary file not shown.

View File

@ -0,0 +1,72 @@
/*
* \brief Support functions for drawing outlined labels
* \author Norman Feske
* \date 2006-08-22
*/
/*
* Copyright (C) 2006-2013 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 _DRAW_LABEL_H_
#define _DRAW_LABEL_H_
#include "canvas.h"
extern Text_painter::Font default_font;
/*
* Gap between session label and view title in pixels
*/
enum { LABEL_GAP = 5 };
/**
* Draw black outline of string
*/
inline void draw_string_outline(Canvas_base &canvas, Point pos, char const *s)
{
for (int j = -1; j <= 1; j++)
for (int i = -1; i <= 1; i++)
if (i || j)
canvas.draw_text(pos + Point(i, j), default_font, BLACK, s);
}
/**
* Return bounding box of composed label displayed with the default font
*
* \param sl session label string
* \param vt view title string
*/
inline Area label_size(const char *sl, const char *vt) {
return Area(default_font.str_w(sl) + LABEL_GAP + default_font.str_w(vt) + 2,
default_font.str_h(sl) + 2); }
/**
* Draw outlined view label
*
* View labels are composed of two parts: the session label and the view title.
* The unforgeable session label is defined on session creation by system
* policy. In contrast, the view title can individually be defined by the
* application.
*/
static inline void draw_label(Canvas_base &canvas, Point pos,
char const *session_label, Color session_label_color,
char const *view_title, Color view_title_color)
{
pos = pos + Point(1, 1);
draw_string_outline(canvas, pos, session_label);
canvas.draw_text(pos, default_font, session_label_color, session_label);
pos = pos + Point(default_font.str_w(session_label) + LABEL_GAP, 0);
draw_string_outline(canvas, pos, view_title);
canvas.draw_text(pos, default_font, view_title_color, view_title);
}
#endif /* _DRAW_LABEL_H_ */

View File

@ -0,0 +1,90 @@
/*
* \brief Global keys handling
* \author Norman Feske
* \date 2013-09-07
*/
/*
* Copyright (C) 2013 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/config.h>
/* local includes */
#include "global_keys.h"
Global_keys::Policy *Global_keys::_lookup_policy(char const *key_name)
{
for (unsigned i = 0; i < NUM_POLICIES; i++)
if (Genode::strcmp(key_name, Input::key_name((Input::Keycode)i)) == 0)
return &_policies[i];
return 0;
}
void Global_keys::apply_config(Session_list &session_list)
{
for (unsigned i = 0; i < NUM_POLICIES; i++)
_policies[i].undefine();
using Genode::Xml_node;
try {
Xml_node node = Genode::config()->xml_node().sub_node("global-keys").sub_node("key");
for (; ; node = node.next("key")) {
if (!node.has_attribute("name")) {
PWRN("attribute 'name' missing in <key> config node");
continue;
}
char name[32]; name[0] = 0;
node.attribute("name").value(name, sizeof(name));
Policy * policy = _lookup_policy(name);
if (!policy) {
PWRN("invalid key name \"%s\"", name);
continue;
}
/* if two policies match, give precedence to policy defined first */
if (policy->defined())
continue;
if (node.has_attribute("operation")) {
Xml_node::Attribute operation = node.attribute("operation");
if (operation.has_value("kill")) {
policy->operation_kill();
continue;
} else if (operation.has_value("xray")) {
policy->operation_xray();
continue;
} else {
char buf[32]; buf[0] = 0;
operation.value(buf, sizeof(buf));
PWRN("unknown operation \"%s\" for key %s", buf, name);
}
continue;
}
if (!node.has_attribute("label")) {
PWRN("missing 'label' attribute for key %s", name);
continue;
}
/* assign policy to matching client session */
for (Session *s = session_list.first(); s; s = s->next())
if (node.attribute("label").has_value(s->label().string()))
policy->client(s);
}
} catch (Xml_node::Nonexistent_sub_node) { }
}

View File

@ -0,0 +1,96 @@
/*
* \brief Global keys policy
* \author Norman Feske
* \date 2013-09-06
*/
/*
* Copyright (C) 2013 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 _GLOBAL_KEYS_H_
#define _GLOBAL_KEYS_H_
/* Genode includes */
#include <input/keycodes.h>
/* local includes */
#include "session.h"
class Global_keys
{
private:
struct Policy
{
enum Type {
/**
* Key is not global but should be propagated to focused client
*/
UNDEFINED,
/**
* Key activates nitpicker's built-in kill mode
*/
KILL,
/**
* Key activates nitpicker's built-in X-ray mode
*/
XRAY,
/**
* Key should be propagated to client session
*/
CLIENT
};
Type _type;
Session *_session;
Policy() : _type(UNDEFINED), _session(0) { }
void undefine() { _type = UNDEFINED; _session = 0; }
void operation_kill() { _type = KILL; _session = 0; }
void operation_xray() { _type = XRAY; _session = 0; }
void client(Session *s) { _type = CLIENT; _session = s; }
bool defined() const { return _type != UNDEFINED; }
bool xray() const { return _type == XRAY; }
bool kill() const { return _type == KILL; }
};
enum { NUM_POLICIES = Input::KEY_MAX + 1 };
Policy _policies[NUM_POLICIES];
/**
* Lookup policy that matches the specified key name
*/
Policy *_lookup_policy(char const *key_name);
bool _valid(Input::Keycode key) const {
return key >= 0 && key <= Input::KEY_MAX; }
public:
Session *global_receiver(Input::Keycode key) {
return _valid(key) ? _policies[key]._session : 0; }
void apply_config(Session_list &session_list);
bool is_operation_key(Input::Keycode key) const {
return _valid(key) && (_policies[key].xray() || _policies[key].kill()); }
bool is_xray_key(Input::Keycode key) const {
return _valid(key) && _policies[key].xray(); }
bool is_kill_key(Input::Keycode key) const {
return _valid(key) && _policies[key].kill(); }
};
#endif /* _GLOBAL_KEYS_H_ */

View File

@ -0,0 +1,98 @@
/*
* \brief Input handling utilities
* \author Norman Feske
* \date 2013-09-07
*/
/*
* Copyright (C) 2013 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 _INPUT_H_
#define _INPUT_H_
/* Genode includes */
#include <input/event.h>
/* local includes */
#include "user_state.h"
/**
* Determine number of events that can be merged into one
*
* \param ev pointer to first event array element to check
* \param max size of the event array
* \return number of events subjected to merge
*/
static unsigned num_consecutive_events(Input::Event const *ev, unsigned max)
{
if (max < 1) return 0;
if (ev->type() != Input::Event::MOTION) return 1;
bool first_is_absolute = ev->is_absolute_motion();
/* iterate until we get a different event type, start at second */
unsigned cnt = 1;
for (ev++ ; cnt < max; cnt++, ev++) {
if (ev->type() != Input::Event::MOTION) break;
if (first_is_absolute != ev->is_absolute_motion()) break;
}
return cnt;
}
/**
* Merge consecutive motion events
*
* \param ev event array to merge
* \param n number of events to merge
* \return merged motion event
*/
static Input::Event merge_motion_events(Input::Event const *ev, unsigned n)
{
Input::Event res;
for (unsigned i = 0; i < n; i++, ev++)
res = Input::Event(Input::Event::MOTION, 0, ev->ax(), ev->ay(),
res.rx() + ev->rx(), res.ry() + ev->ry());
return res;
}
static void import_input_events(Input::Event *ev_buf, unsigned num_ev,
User_state &user_state, Canvas_base &canvas)
{
/*
* Take events from input event buffer, merge consecutive motion
* events, and pass result to the user state.
*/
for (unsigned src_ev_cnt = 0; src_ev_cnt < num_ev; src_ev_cnt++) {
Input::Event *e = &ev_buf[src_ev_cnt];
Input::Event curr = *e;
if (e->type() == Input::Event::MOTION) {
unsigned n = num_consecutive_events(e, num_ev - src_ev_cnt);
curr = merge_motion_events(e, n);
/* skip merged events */
src_ev_cnt += n - 1;
}
/*
* If subsequential relative motion events are merged to
* a zero-motion event, drop it. Otherwise, it would be
* misinterpreted as absolute event pointing to (0, 0).
*/
if (e->is_relative_motion() && curr.rx() == 0 && curr.ry() == 0)
continue;
/* pass event to user state */
user_state.handle_event(curr, canvas);
}
}
#endif /* _INPUT_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,51 @@
/*
* \brief Nitpicker menubar interface
* \author Norman Feske
* \date 2006-08-22
*/
/*
* Copyright (C) 2006-2013 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 _MENUBAR_H_
#define _MENUBAR_H_
#include "view.h"
#include "draw_label.h"
#include "mode.h"
struct Menubar_state
{
Genode::String<128> session_label;
Genode::String<128> view_title;
Mode mode;
Color session_color;
Menubar_state(Mode mode, char const *session_label,
char const *view_title, Color session_color)
:
session_label(session_label), view_title(view_title),
mode(mode), session_color(session_color)
{ }
Menubar_state() : session_color(BLACK) { }
};
struct Menubar : Menubar_state
{
virtual ~Menubar() { }
/**
* Set state that is displayed in the trusted menubar
*/
virtual void state(Menubar_state) = 0;
Menubar_state state() const { return *this; }
};
#endif

View File

@ -0,0 +1,61 @@
/*
* \brief Nitpicker mode
* \author Norman Feske
* \date 2006-08-22
*/
/*
* Copyright (C) 2006-2013 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 _MODE_H_
#define _MODE_H_
class View;
class Canvas_base;
class Mode
{
private:
bool _xray;
bool _kill;
/*
* Last clicked view. This view is receiving keyboard input, except
* for global keys.
*/
View const *_focused_view;
public:
Mode(): _xray(false), _kill(false), _focused_view(0) { }
virtual ~Mode() { }
/**
* Accessors
*/
bool xray() const { return _xray; }
bool kill() const { return _kill; }
bool flat() const { return !_xray && !_kill; }
void leave_kill() { _kill = false; }
void toggle_kill() { _kill = !_kill; }
void toggle_xray() { _xray = !_xray; }
View const *focused_view() const { return _focused_view; }
void focused_view(View const *view) { _focused_view = view; }
/**
* Discard all references to specified view
*/
virtual void forget(Canvas_base &, View const &v) {
if (&v == _focused_view) _focused_view = 0; }
};
#endif

View File

@ -0,0 +1,80 @@
/*
* \brief Nitpicker mouse cursor
* \author Norman Feske
* \date 2006-08-18
*
* The Nitpicker mouse cursor is implemented as a transparent
* view that stays always in front of all other views.
*/
/*
* Copyright (C) 2006-2013 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 _MOUSE_CURSOR_H_
#define _MOUSE_CURSOR_H_
#include <nitpicker_gfx/texture_painter.h>
#include "view.h"
#include "session.h"
template <typename PT>
class Mouse_cursor : public Texture<PT>,
public Session, public View
{
private:
View_stack const &_view_stack;
public:
/**
* Constructor
*/
Mouse_cursor(PT const *pixels, Area size, View_stack const &view_stack)
:
Texture<PT>(pixels, 0, size),
Session(Genode::Session_label(""), 0, false),
View(*this, View::STAY_TOP, View::TRANSPARENT, View::NOT_BACKGROUND, 0),
_view_stack(view_stack)
{ }
/***********************
** Session interface **
***********************/
void submit_input_event(Input::Event) { }
/********************
** View interface **
********************/
/*
* The mouse cursor is always displayed without a surrounding frame.
*/
int frame_size(Mode const &mode) const { return 0; }
void frame(Canvas_base &canvas, Mode const &mode) const { }
void draw(Canvas_base &canvas, Mode const &mode) const
{
Rect const view_rect = abs_geometry();
Clip_guard clip_guard(canvas, view_rect);
/* draw area behind the mouse cursor */
_view_stack.draw_rec(canvas, view_stack_next(), 0, 0, view_rect);
/* draw mouse cursor */
canvas.draw_texture(view_rect.p1(), *this, Texture_painter::MASKED, BLACK, true);
}
};
#endif

View File

@ -0,0 +1,141 @@
/*
* \brief Nitpicker session interface
* \author Norman Feske
* \date 2006-08-09
*/
/*
* Copyright (C) 2006-2013 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 _SESSION_H_
#define _SESSION_H_
/* Genode includes */
#include <util/list.h>
#include <util/string.h>
#include <os/session_policy.h>
/* local includes */
#include "color.h"
#include "canvas.h"
class View;
class Session;
namespace Input { class Event; }
typedef Genode::List<Session> Session_list;
class Session : public Session_list::Element
{
private:
Genode::Session_label const _label;
Color _color;
Texture_base const *_texture = { 0 };
bool _uses_alpha = { false };
View *_background = 0;
int _v_offset;
unsigned char const *_input_mask = { 0 };
bool const _stay_top;
public:
/**
* Constructor
*
* \param label session label
* \param v_offset vertical screen offset of session
* \param stay_top true for views that stay always in front
*/
Session(Genode::Session_label const &label, int v_offset, bool stay_top)
:
_label(label), _v_offset(v_offset), _stay_top(stay_top)
{ }
virtual ~Session() { }
virtual void submit_input_event(Input::Event ev) = 0;
Genode::Session_label const &label() const { return _label; }
Texture_base const *texture() const { return _texture; }
void texture(Texture_base const *texture, bool uses_alpha)
{
_texture = texture;
_uses_alpha = uses_alpha;
}
/**
* Set input mask buffer
*
* \param mask input mask buffer containing a byte value per texture
* pixel, which describes the policy of handling user
* input referring to the pixel. If set to zero, the input
* is passed through the view such that it can be handled
* by one of the subsequent views in the view stack. If
* set to one, the input is consumed by the view. If
* 'input_mask' is a null pointer, user input is
* unconditionally consumed by the view.
*/
void input_mask(unsigned char const *mask) { _input_mask = mask; }
Color color() const { return _color; }
View *background() const { return _background; }
void background(View *background) { _background = background; }
bool stay_top() const { return _stay_top; }
/**
* Return true if session uses an alpha channel
*/
bool uses_alpha() const { return _texture && _uses_alpha; }
/**
* Return vertical offset of session
*/
int v_offset() const { return _v_offset; }
/**
* Return input mask value at specified buffer position
*/
unsigned char input_mask_at(Point p) const
{
if (!_input_mask || !_texture) return 0;
/* check boundaries */
if ((unsigned)p.x() >= _texture->size().w()
|| (unsigned)p.y() >= _texture->size().h())
return 0;
return _input_mask[p.y()*_texture->size().w() + p.x()];
}
/**
* Set session color according to the list of configured policies
*
* Select the policy that matches the label. If multiple policies
* match, select the one with the largest number of characters.
*/
void apply_session_color()
{
/* use white by default */
_color = WHITE;
try {
Genode::Session_policy policy(_label);
/* read color attribute */
policy.attribute("color").value(&_color);
} catch (...) { }
}
};
#endif

View File

@ -0,0 +1,9 @@
TARGET = nitpicker
LIBS = base blit config server
SRC_CC = main.cc \
view_stack.cc \
view.cc \
user_state.cc \
global_keys.cc
SRC_BIN = default.tff

View File

@ -0,0 +1,294 @@
/*
* \brief User state implementation
* \author Norman Feske
* \date 2006-08-27
*/
/*
* Copyright (C) 2006-2013 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 <input/event.h>
#include <input/keycodes.h>
#include "user_state.h"
using namespace Input;
/***************
** Utilities **
***************/
static inline bool _masked_key(Global_keys &global_keys, Keycode keycode) {
return global_keys.is_kill_key(keycode) || global_keys.is_xray_key(keycode); }
static inline bool _mouse_button(Keycode keycode) {
return keycode >= BTN_LEFT && keycode <= BTN_MIDDLE; }
/**************************
** User state interface **
**************************/
User_state::User_state(Global_keys &global_keys, Area view_stack_size, Menubar &menubar)
:
View_stack(view_stack_size, *this), _global_keys(global_keys), _key_cnt(0),
_menubar(menubar), _pointed_view(0), _input_receiver(0),
_global_key_sequence(false)
{ }
void User_state::handle_event(Input::Event ev, Canvas_base &canvas)
{
Input::Keycode const keycode = ev.keycode();
Input::Event::Type const type = ev.type();
/*
* Mangle incoming events
*/
int ax = _mouse_pos.x(), ay = _mouse_pos.y();
int rx = 0, ry = 0; /* skip info about relative motion by default */
/* transparently handle absolute and relative motion events */
if (type == Event::MOTION) {
if ((ev.rx() || ev.ry()) && ev.ax() == 0 && ev.ay() == 0) {
ax = Genode::max(0, Genode::min((int)size().w(), ax + ev.rx()));
ay = Genode::max(0, Genode::min((int)size().h(), ay + ev.ry()));
} else {
ax = ev.ax();
ay = ev.ay();
}
}
/* propagate relative motion for wheel events */
if (type == Event::WHEEL) {
rx = ev.rx();
ry = ev.ry();
}
/* create the mangled event */
ev = Input::Event(type, keycode, ax, ay, rx, ry);
_mouse_pos = Point(ax, ay);
/* count keys */
if (type == Event::PRESS) _key_cnt++;
if (type == Event::RELEASE && _key_cnt > 0) _key_cnt--;
View const * const pointed_view = find_view(_mouse_pos);
/*
* Deliver a leave event if pointed-to session changed
*/
if (pointed_view && _pointed_view &&
!pointed_view->same_session_as(*_pointed_view)) {
Input::Event leave_ev(Input::Event::LEAVE, 0, ax, ay, 0, 0);
_pointed_view->session().submit_input_event(leave_ev);
}
_pointed_view = pointed_view;
/**
* Guard that, when 'enabled' is set to true, performs a whole-screen
* update when leaving the scope
*/
struct Update_all_guard
{
User_state &user_state;
Canvas_base &canvas;
bool update_menubar = false;
bool update_views = false;
char const *menu_title = "";
Update_all_guard(User_state &user_state, Canvas_base &canvas)
: user_state(user_state), canvas(canvas) { }
~Update_all_guard()
{
Menubar_state state(user_state, "", "", BLACK);
if (user_state._input_receiver)
state = Menubar_state(user_state,
user_state._input_receiver->label().string(),
menu_title,
user_state._input_receiver->color());
if (update_menubar)
user_state._menubar.state(state);
if (update_menubar || update_views)
user_state.update_all_views(canvas);
}
} update_all_guard(*this, canvas);
/*
* Handle start of a key sequence
*/
if (type == Event::PRESS && _key_cnt == 1) {
/*
* Detect mouse press event in kill mode, used to select the session
* to lock out.
*/
if (kill() && keycode == Input::BTN_LEFT) {
if (pointed_view)
lock_out_session(canvas, pointed_view->session());
/* leave kill mode */
update_all_guard.update_menubar = true;
update_all_guard.update_views = true;
Mode::leave_kill();
return;
}
/* update focused view */
if (pointed_view != focused_view() && _mouse_button(keycode)) {
bool const focus_stays_in_session =
focused_view() && pointed_view &&
focused_view()->belongs_to(pointed_view->session());
/*
* Update the whole screen when the focus change results in
* changing the focus to another session.
*/
if (flat() && !focus_stays_in_session) {
update_all_guard.update_menubar = true;
update_all_guard.update_views = true;
}
/*
* Notify both the old focussed session and the new one.
*/
if (!focus_stays_in_session) {
if (focused_view()) {
Input::Event unfocus_ev(Input::Event::FOCUS, 0, ax, ay, 0, 0);
focused_view()->session().submit_input_event(unfocus_ev);
}
if (pointed_view) {
Input::Event focus_ev(Input::Event::FOCUS, 1, ax, ay, 0, 0);
pointed_view->session().submit_input_event(focus_ev);
}
}
update_all_guard.update_menubar = true;
if (!flat() || !focused_view() || !pointed_view)
update_all_guard.update_views = true;
focused_view(pointed_view);
}
/*
* If there exists a global rule for the pressed key, set the
* corresponding session as receiver of the input stream until the key
* count reaches zero. Otherwise, the input stream is directed to the
* pointed-at view.
*
* If we deliver a global key sequence, we temporarily change the focus
* to the global receiver. To reflect that change, we need to update
* the whole screen.
*/
Session * const global_receiver = _global_keys.global_receiver(keycode);
if (global_receiver) {
_global_key_sequence = true;
_input_receiver = global_receiver;
update_all_guard.menu_title = "";
update_all_guard.update_menubar = true;
update_all_guard.update_views = true;
}
/*
* No global rule matched, so the input stream gets directed to the
* focused view or refers to a built-in operation.
*/
if (!global_receiver && focused_view()) {
_input_receiver = &focused_view()->session();
update_all_guard.menu_title = focused_view()->title();
}
/*
* Toggle kill and xray modes. If one of those keys is pressed,
* suppress the delivery to clients.
*/
if (_global_keys.is_operation_key(keycode)) {
if (_global_keys.is_kill_key(keycode)) Mode::toggle_kill();
if (_global_keys.is_xray_key(keycode)) Mode::toggle_xray();
update_all_guard.update_menubar = true;
update_all_guard.update_views = true;
_input_receiver = 0;
}
}
/*
* Deliver event to session except when kill mode is activated
*/
if (kill()) return;
if (type == Event::MOTION || type == Event::WHEEL) {
if (_key_cnt == 0) {
/*
* In flat mode, we deliver motion events to the session of
* the pointed view. In xray mode, we deliver motion
* events only to the session with the focused view.
*/
if (flat() || (xray() && focused_view() == pointed_view))
if (pointed_view)
pointed_view->session().submit_input_event(ev);
} else if (_input_receiver)
_input_receiver->submit_input_event(ev);
}
/* deliver press/release event to session with focused view */
if (type == Event::PRESS || type == Event::RELEASE)
if (_input_receiver)
_input_receiver->submit_input_event(ev);
/*
* Detect end of global key sequence
*/
if (ev.type() == Event::RELEASE && _key_cnt == 0 && _global_key_sequence) {
_input_receiver = focused_view() ? &focused_view()->session() : 0;
update_all_guard.menu_title = focused_view() ? focused_view()->title() : "";
update_all_guard.update_menubar = true;
update_all_guard.update_views = true;
_global_key_sequence = false;
}
}
/********************
** Mode interface **
********************/
void User_state::forget(Canvas_base &canvas, View const &view)
{
if (focused_view() == &view) {
Mode::forget(canvas, view);
_menubar.state(Menubar_state(*this, "", "", BLACK));
update_all_views(canvas);
}
if (_input_receiver && view.belongs_to(*_input_receiver))
_input_receiver = 0;
if (_pointed_view == &view)
_pointed_view = find_view(_mouse_pos);
}

View File

@ -0,0 +1,95 @@
/*
* \brief Nitpicker user state handling
* \author Norman Feske
* \date 2006-08-09
*
* This class comprehends the policy of user interaction.
* It manages the toggling of Nitpicker's different modes
* and routes input events to corresponding client sessions.
*/
/*
* Copyright (C) 2006-2013 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 _USER_STATE_H_
#define _USER_STATE_H_
#include "mode.h"
#include "menubar.h"
#include "view_stack.h"
#include "global_keys.h"
class User_state : public Mode, public View_stack
{
private:
/*
* Policy for the routing of global keys
*/
Global_keys &_global_keys;
/*
* Number of currently pressed keys.
* This counter is used to determine if the user
* is dragging an item.
*/
unsigned _key_cnt;
/*
* Menubar to display trusted labeling information
* according to the current Mitpicker mode and the
* focused view.
*/
Menubar &_menubar;
/*
* Current mouse cursor position
*/
Point _mouse_pos;
/*
* Currently pointed-at view
*/
View const *_pointed_view;
/*
* Session that receives the current stream of input events
*/
Session *_input_receiver;
/*
* True while a global key sequence is processed
*/
bool _global_key_sequence;
public:
/**
* Constructor
*/
User_state(Global_keys &, Area view_stack_size, Menubar &);
/**
* Handle input event
*
* This function controls the Nitpicker mode and the
* user state variables.
*/
void handle_event(Input::Event ev, Canvas_base &);
/**
* Accessors
*/
Point mouse_pos() { return _mouse_pos; }
/**
* Mode interface
*/
void forget(Canvas_base &, View const &) override;
};
#endif

View File

@ -0,0 +1,129 @@
/*
* \brief Nitpicker view implementation
* \author Norman Feske
* \date 2006-08-09
*/
/*
* Copyright (C) 2006-2013 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 <base/printf.h>
#include <os/pixel_rgb565.h>
#include <nitpicker_gfx/texture_painter.h>
#include <nitpicker_gfx/box_painter.h>
#include "view.h"
#include "clip_guard.h"
#include "session.h"
#include "draw_label.h"
/***************
** Utilities **
***************/
/**
* Draw rectangle
*/
static void draw_rect(Canvas_base &canvas, int x, int y, int w, int h,
Color color)
{
canvas.draw_box(Rect(Point(x, y), Area(w, 1)), color);
canvas.draw_box(Rect(Point(x, y), Area(1, h)), color);
canvas.draw_box(Rect(Point(x + w - 1, y), Area(1, h)), color);
canvas.draw_box(Rect(Point(x, y + h - 1), Area(w, 1)), color);
}
/**
* Draw outlined frame with black outline color
*/
static void draw_frame(Canvas_base &canvas, Rect r, Color color,
int frame_size)
{
/* draw frame around the view */
int d = frame_size;
draw_rect(canvas, r.x1() - d, r.y1() - d, r.w() + 2*d, r.h() + 2*d, BLACK);
while (--d > 1)
draw_rect(canvas, r.x1() - d, r.y1() - d, r.w() + 2*d, r.h() + 2*d, color);
draw_rect(canvas, r.x1() - d, r.y1() - d, r.w() + 2*d, r.h() + 2*d, BLACK);
}
/**********
** View **
**********/
void View::title(const char *title)
{
Genode::strncpy(_title, title, TITLE_LEN);
/* calculate label size, the position is defined by the view stack */
_label_rect = Rect(Point(0, 0), label_size(_session.label().string(), _title));
}
void View::frame(Canvas_base &canvas, Mode const &mode) const
{
/* do not draw frame in flat mode */
if (mode.flat()) return;
draw_frame(canvas, abs_geometry(), _session.color(), frame_size(mode));
}
void View::draw(Canvas_base &canvas, Mode const &mode) const
{
/* is this the currently focused view? */
bool const view_is_focused = mode.focused_view()
&& mode.focused_view()->belongs_to(_session);
Color const frame_color = _session.color();
/*
* Use dimming in x-ray and kill mode, but do not dim the focused view in
* x-ray mode.
*/
Texture_painter::Mode const op = mode.flat() || (mode.xray() && view_is_focused)
? Texture_painter::SOLID : Texture_painter::MIXED;
Rect const view_rect = abs_geometry();
/*
* The view content and label should never overdraw the
* frame of the view in non-flat Nitpicker modes. The frame
* is located outside the view area. By shrinking the
* clipping area to the view area, we protect the frame.
*/
Clip_guard clip_guard(canvas, view_rect);
/*
* If the clipping area shrinked to zero, we do not process drawing
* operations.
*/
if (!canvas.clip().valid() || !&_session) return;
/* allow alpha blending only in flat mode */
bool allow_alpha = mode.flat();
/* draw view content */
Color const mix_color = mode.kill() ? KILL_COLOR
: Color(_session.color().r >> 1,
_session.color().g >> 1,
_session.color().b >> 1);
if (_session.texture())
canvas.draw_texture(_buffer_off + view_rect.p1(), *_session.texture(),
op, mix_color, allow_alpha);
if (mode.flat()) return;
/* draw label */
draw_label(canvas, _label_rect.p1(), _session.label().string(), WHITE,
_title, frame_color);
}

View File

@ -0,0 +1,217 @@
/*
* \brief Nitpicker view interface
* \author Norman Feske
* \date 2006-08-08
*/
/*
* Copyright (C) 2006-2013 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 _VIEW_H_
#define _VIEW_H_
#include <util/string.h>
#include <util/list.h>
#include "mode.h"
#include "session.h"
class Buffer;
/*
* For each buffer, there is a list of views that belong to
* this buffer. This list is called Same_buffer_list.
*/
struct Same_buffer_list_elem : Genode::List<Same_buffer_list_elem>::Element { };
struct View_stack_elem : Genode::List<View_stack_elem>::Element { };
/*
* If a view has a parent, it is a list element of its parent view
*/
struct View_parent_elem : Genode::List<View_parent_elem>::Element { };
class View : public Same_buffer_list_elem,
public View_stack_elem,
public View_parent_elem
{
public:
enum { TITLE_LEN = 32 }; /* max.characters of a title */
enum Stay_top { NOT_STAY_TOP = 0, STAY_TOP = 1 };
enum Transparent { NOT_TRANSPARENT = 0, TRANSPARENT = 1 };
enum Background { NOT_BACKGROUND = 0, BACKGROUND = 1 };
private:
Stay_top const _stay_top; /* keep view always on top */
Transparent const _transparent; /* background is partly visible */
Background _background; /* view is a background view */
View *_parent; /* parent view */
Rect _geometry; /* position and size relative to parent */
Rect _label_rect; /* position and size of label */
Point _buffer_off; /* offset to the visible buffer area */
Session &_session; /* session that created the view */
char _title[TITLE_LEN];
Genode::List<View_parent_elem> _children;
public:
View(Session &session, Stay_top stay_top, Transparent transparent,
Background bg, View *parent)
:
_stay_top(stay_top), _transparent(transparent), _background(bg),
_parent(parent), _session(session)
{ title(""); }
virtual ~View()
{
/* break link to our parent */
if (_parent)
_parent->remove_child(this);
/* break links to our children */
while (View_parent_elem *e = _children.first())
static_cast<View *>(e)->dissolve_from_parent();
}
/**
* Return absolute view position
*/
Point abs_position() const
{
return _parent ? _geometry.p1() + _parent->abs_position()
: _geometry.p1();
}
/**
* Return absolute view geometry
*/
Rect abs_geometry() const
{
return Rect(abs_position(), _geometry.area());
}
/**
* Break the relationship of a child view from its parent
*
* This function is called when a parent view gets destroyed.
*/
void dissolve_from_parent()
{
_parent = 0;
_geometry = Rect();
}
Rect geometry() const { return _geometry; }
void geometry(Rect geometry) { _geometry = geometry; }
void add_child(View const *child) { _children.insert(child); }
void remove_child(View const *child) { _children.remove(child); }
template <typename FN>
void for_each_child(FN const &fn) const {
for (View_parent_elem const *e = _children.first(); e; e = e->next())
fn(*static_cast<View const *>(e));
}
/**
* Return thickness of frame that surrounds the view
*/
virtual int frame_size(Mode const &mode) const
{
if (mode.focused_view()
&& mode.focused_view()->belongs_to(_session))
return 5;
return 3;
}
/**
* Draw view-surrounding frame on canvas
*/
virtual void frame(Canvas_base &canvas, Mode const &mode) const;
/**
* Draw view on canvas
*/
virtual void draw(Canvas_base &canvas, Mode const &mode) const;
/**
* Set view title
*/
void title(const char *title);
/**
* Return successor in view stack
*/
View const *view_stack_next() const {
return static_cast<View const *>(View_stack_elem::next()); }
View *view_stack_next() {
return static_cast<View *>(View_stack_elem::next()); }
/**
* Set view as background
*
* \param is_bg true if view is background
*/
void background(bool is_bg) {
_background = is_bg ? BACKGROUND : NOT_BACKGROUND; }
/**
* Accessors
*/
Session &session() const { return _session; }
bool belongs_to(Session const &session) const { return &session == &_session; }
bool same_session_as(View const &other) const { return &_session == &other._session; }
bool top_level() const { return _parent == 0; }
bool stay_top() const { return _stay_top; }
bool transparent() const { return _transparent || _session.uses_alpha(); }
bool background() const { return _background; }
Rect label_rect() const { return _label_rect; }
bool uses_alpha() const { return _session.uses_alpha(); }
Point buffer_off() const { return _buffer_off; }
char const *title() const { return _title; }
void buffer_off(Point buffer_off) { _buffer_off = buffer_off; }
void label_pos(Point pos) { _label_rect = Rect(pos, _label_rect.area()); }
/**
* Return true if input at screen position 'p' refers to the view
*/
bool input_response_at(Point p, Mode const &mode) const
{
Rect const view_rect = abs_geometry();
/* check if point lies outside view geometry */
if ((p.x() < view_rect.x1()) || (p.x() > view_rect.x2())
|| (p.y() < view_rect.y1()) || (p.y() > view_rect.y2()))
return false;
/* if view uses an alpha channel, check the input mask */
if (mode.flat() && session().uses_alpha())
return session().input_mask_at(p - view_rect.p1() + _buffer_off);
return true;
}
};
#endif /* _VIEW_H_ */

View File

@ -0,0 +1,323 @@
/*
* \brief Nitpicker view stack implementation
* \author Norman Feske
* \date 2006-08-09
*/
/*
* Copyright (C) 2006-2013 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 "view_stack.h"
#include "clip_guard.h"
/***************
** Utilities **
***************/
/**
* Get last view with the stay_top attribute
*
* \param view first view of the view stack
*/
static View const *last_stay_top_view(View const *view)
{
for (; view && view->view_stack_next() && view->view_stack_next()->stay_top(); )
view = view->view_stack_next();
return view;
}
/**************************
** View stack interface **
**************************/
template <typename VIEW>
VIEW *View_stack::_next_view(VIEW *view) const
{
Session * const active_session = _mode.focused_view() ?
&_mode.focused_view()->session() : 0;
View * const active_background = active_session ?
active_session->background() : 0;
for (;;) {
view = view ? view->view_stack_next() : 0;
/* check if we hit the bottom of the view stack */
if (!view) return 0;
if (!view->background()) return view;
if (is_default_background(view) || view == active_background)
return view;
/* view is a background view belonging to a non-focused session */
}
return 0;
}
Rect View_stack::_outline(View const &view) const
{
Rect const rect = view.abs_geometry();
if (_mode.flat()) return rect;
/* request thickness of view frame */
int const frame_size = view.frame_size(_mode);
return Rect(Point(rect.x1() - frame_size, rect.y1() - frame_size),
Point(rect.x2() + frame_size, rect.y2() + frame_size));
}
View const *View_stack::_target_stack_position(View const *neighbor, bool behind)
{
/* find target position in view stack */
View const *cv = last_stay_top_view(_first_view());
for ( ; cv; cv = _next_view(cv)) {
/* bring view to front? */
if (behind && !neighbor)
break;
/* insert view after cv? */
if (behind && (cv == neighbor))
break;
/* insert view in front of cv? */
if (!behind && (_next_view(cv) == neighbor))
break;
/* insert view in front of the background? */
if (!behind && !neighbor && _next_view(cv)->background())
break;
}
return cv ? cv : last_stay_top_view(_first_view());
}
void View_stack::_optimize_label_rec(View const *cv, View const *lv, Rect rect, Rect *optimal)
{
/* if label already fits in optimized rectangle, we are happy */
if (optimal->fits(lv->label_rect().area()))
return;
/* find next view that intersects with the rectangle or the target view */
Rect clipped;
while (cv && cv != lv && !(clipped = Rect::intersect(_outline(*cv), rect)).valid())
cv = _next_view(cv);
/* reached end of view stack */
if (!cv) return;
if (cv != lv && _next_view(cv)) {
/* cut current view from rectangle and go into sub rectangles */
Rect r[4];
rect.cut(clipped, &r[0], &r[1], &r[2], &r[3]);
for (int i = 0; i < 4; i++)
_optimize_label_rec(_next_view(cv), lv, r[i], optimal);
return;
}
/*
* Now, cv equals lv and we must decide how to configure the
* optimal rectangle.
*/
/* stop if label does not fit vertically */
if (rect.h() < lv->label_rect().h())
return;
/*
* If label fits completely within current rectangle, we are done.
* If label's width is not fully visible, choose the widest rectangle.
*/
if (rect.fits(lv->label_rect().area()) || (rect.w() > optimal->w())) {
*optimal = rect;
return;
}
}
void View_stack::_place_labels(Canvas_base &canvas, Rect rect)
{
/* do not calculate label positions in flat mode */
if (_mode.flat()) return;
/* ignore mouse cursor */
View const *start = _next_view(_first_view());
View *view = _next_view(_first_view());
for (; view && _next_view(view); view = _next_view(view)) {
Rect const view_rect = view->abs_geometry();
if (Rect::intersect(view_rect, rect).valid()) {
Rect old = view->label_rect(), best;
/* calculate best visible label position */
Rect rect = Rect::intersect(Rect(Point(), canvas.size()), view_rect);
if (start) _optimize_label_rec(start, view, rect, &best);
/*
* If label is not fully visible, we ensure to display the first
* (most important) part. Otherwise, we center the label horizontally.
*/
int x = best.x1();
if (best.fits(view->label_rect().area()))
x += (best.w() - view->label_rect().w()) / 2;
view->label_pos(Point(x, best.y1()));
/* refresh old and new label positions */
refresh_view(canvas, *view, view, old);
refresh_view(canvas, *view, view, view->label_rect());
}
}
}
void View_stack::draw_rec(Canvas_base &canvas, View const *view, View const *dst_view,
Session const *exclude, Rect rect) const
{
Rect clipped;
/* find next view that intersects with the current clipping rectangle */
for ( ; view && !(clipped = Rect::intersect(_outline(*view), rect)).valid(); )
view = _next_view(view);
/* check if we hit the bottom of the view stack */
if (!view) return;
Rect top, left, right, bottom;
rect.cut(clipped, &top, &left, &right, &bottom);
View const *next = _next_view(view);
/* draw areas at the top/left of the current view */
if (next && top.valid()) draw_rec(canvas, next, dst_view, exclude, top);
if (next && left.valid()) draw_rec(canvas, next, dst_view, exclude, left);
/* draw current view */
if (!dst_view || (dst_view == view) || view->transparent()) {
Clip_guard clip_guard(canvas, clipped);
/* draw background if view is transparent */
if (view->uses_alpha())
draw_rec(canvas, _next_view(view), 0, 0, clipped);
view->frame(canvas, _mode);
if (!exclude || !view->belongs_to(*exclude))
view->draw(canvas, _mode);
}
/* draw areas at the bottom/right of the current view */
if (next && right.valid()) draw_rec(canvas, next, dst_view, exclude, right);
if (next && bottom.valid()) draw_rec(canvas, next, dst_view, exclude, bottom);
}
void View_stack::refresh_view(Canvas_base &canvas, View const &view,
View const *dst_view, Rect rect)
{
/* clip argument agains view outline */
rect = Rect::intersect(rect, _outline(view));
draw_rec(canvas, _first_view(), dst_view, 0, rect);
}
void View_stack::viewport(Canvas_base &canvas, View &view, Rect rect,
Point buffer_off, bool do_redraw)
{
Rect const old_compound = _compound_outline(view);
view.geometry(Rect(rect));
view.buffer_off(buffer_off);
Rect const new_compound = _compound_outline(view);
Rect const compound = Rect::compound(old_compound, new_compound);
/* update labels (except when moving the mouse cursor) */
if (&view != _first_view())
_place_labels(canvas, compound);
if (!_mode.flat())
do_redraw = true;
/* update area on screen */
draw_rec(canvas, _first_view(), 0, do_redraw ? 0 : &view.session(), compound);
}
void View_stack::stack(Canvas_base &canvas, View const &view,
View const *neighbor, bool behind, bool do_redraw)
{
_views.remove(&view);
_views.insert(&view, _target_stack_position(neighbor, behind));
_place_labels(canvas, view.abs_geometry());
/* refresh affected screen area */
refresh_view(canvas, view, 0, _outline(view));
}
void View_stack::title(Canvas_base &canvas, View &view, const char *title)
{
view.title(title);
_place_labels(canvas, view.abs_geometry());
refresh_view(canvas, view, 0, _outline(view));
}
View *View_stack::find_view(Point p)
{
/* skip mouse cursor */
View *view = _next_view(_first_view());
for ( ; view; view = _next_view(view))
if (view->input_response_at(p, _mode))
return view;
return 0;
}
void View_stack::remove_view(Canvas_base &canvas, View const &view, bool redraw)
{
/* remember geometry of view to remove */
Rect rect = _outline(view);
/* exclude view from view stack */
_views.remove(&view);
/*
* Reset focused and pointed-at view if necessary
*
* Thus must be done after calling '_views.remove' because the new focused
* pointer is determined by traversing the view stack. If the to-be-removed
* view would still be there, we would re-assign the old pointed-to view as
* the current one, resulting in a dangling pointer right after the view
* gets destructed by the caller of 'removed_view'.
*/
_mode.forget(canvas, view);
/* redraw area where the view was visible */
if (redraw)
draw_rec(canvas, _first_view(), 0, 0, rect);
}

View File

@ -0,0 +1,222 @@
/*
* \brief Nitpicker view stack
* \author Norman Feske
* \date 2006-08-08
*/
/*
* Copyright (C) 2006-2013 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 _VIEW_STACK_H_
#define _VIEW_STACK_H_
#include "view.h"
#include "canvas.h"
class Session;
class View_stack
{
private:
Area _size;
Mode &_mode;
Genode::List<View_stack_elem> _views;
View *_default_background;
/**
* Return outline geometry of a view
*
* This geometry depends on the view geometry and the currently
* active Nitpicker mode. In non-flat modes, we incorporate the
* surrounding frame.
*/
Rect _outline(View const &view) const;
/**
* Return top-most view of the view stack
*/
View *_first_view() { return static_cast<View *>(_views.first()); }
/**
* Find position in view stack for inserting a view
*/
View const *_target_stack_position(View const *neighbor, bool behind);
/**
* Find best visible label position
*/
void _optimize_label_rec(View const *cv, View const *lv, Rect rect, Rect *optimal);
/**
* Position labels that are affected by specified area
*/
void _place_labels(Canvas_base &, Rect);
/**
* Return compound rectangle covering the view and all of its children
*/
Rect _compound_outline(View const &view)
{
Rect rect = _outline(view);
view.for_each_child([&] (View const &child) {
rect = Rect::compound(_outline(child), rect); });
return rect;
}
/**
* Return view following the specified view in the view stack
*
* The function is a template to capture both const and non-const
* usage.
*/
template <typename VIEW>
VIEW *_next_view(VIEW *view) const;
public:
/**
* Constructor
*/
View_stack(Area size, Mode &mode) :
_size(size), _mode(mode), _default_background(0) { }
/**
* Return size
*/
Area size() const { return _size; }
void size(Area size) { _size = size; }
/**
* Draw views in specified area (recursivly)
*
* \param view current view in view stack
* \param dst_view desired view to draw or NULL
* if all views should be drawn
* \param exclude do not draw views of this session
*/
void draw_rec(Canvas_base &, View const *view, View const *dst_view, Session const *exclude, Rect) const;
/**
* Draw whole view stack
*/
void update_all_views(Canvas_base &canvas)
{
_place_labels(canvas, Rect(Point(), _size));
draw_rec(canvas, _first_view(), 0, 0, Rect(Point(), _size));
}
/**
* Update all views belonging to the specified session
*
* \param Session Session that created the view
* \param Rect Buffer area to update
*
* Note: For now, we perform an independent view-stack traversal
* for each view when calling 'refresh_view'. This becomes
* a potentially high overhead with many views. Having
* a tailored 'draw_rec_session' function would overcome
* this problem.
*/
void update_session_views(Canvas_base &canvas, Session const &session, Rect rect)
{
for (View const *view = _first_view(); view; view = view->view_stack_next()) {
if (!view->belongs_to(session))
continue;
/*
* Determine view portion that displays the buffer portion
* specified by 'rect'.
*/
Point const offset = view->abs_position() + view->buffer_off();
Rect const r = Rect::intersect(Rect(rect.p1() + offset,
rect.p2() + offset),
view->abs_geometry());
refresh_view(canvas, *view, view, r);
}
}
/**
* Refresh area within a view
*
* \param view view that should be updated on screen
* \param dst NULL if all views in the specified area should be
* refreshed or 'view' if the refresh should be limited to
* the specified view.
*/
void refresh_view(Canvas_base &, View const &view, View const *dst, Rect);
/**
* Define position and viewport
*
* \param pos position of view on screen
* \param buffer_off view offset of displayed buffer
* \param do_redraw perform screen update immediately
*/
void viewport(Canvas_base &, View &view, Rect pos, Point buffer_off, bool do_redraw);
/**
* Insert view at specified position in view stack
*
* \param behind insert view in front (true) or
* behind (false) the specified neighbor
*
* To insert a view at the top of the view stack, specify
* neighbor = 0 and behind = true. To insert a view at the
* bottom of the view stack, specify neighbor = 0 and
* behind = false.
*/
void stack(Canvas_base &, View const &view, View const *neighbor = 0,
bool behind = true, bool do_redraw = true);
/**
* Set view title
*/
void title(Canvas_base &, View &view, char const *title);
/**
* Find view at specified position
*/
View *find_view(Point);
/**
* Remove view from view stack
*/
void remove_view(Canvas_base &, View const &, bool redraw = true);
/**
* Define default background
*/
void default_background(View &view) { _default_background = &view; }
/**
* Return true if view is the default background
*/
bool is_default_background(View const *view) const { return view == _default_background; }
/**
* Remove all views of specified session from view stack
*
* Rather than removing the views from the view stack, this function moves
* the session views out of the visible screen area.
*/
void lock_out_session(Canvas_base &canvas, Session const &session)
{
View const *view = _first_view(), *next_view = view->view_stack_next();
while (view) {
if (view->belongs_to(session)) remove_view(canvas, *view);
view = next_view;
next_view = view ? view->view_stack_next() : 0;
}
}
};
#endif

View File

@ -0,0 +1,65 @@
This directory contains an implementation of a block-device-partition server.
Behavior
--------
The server uses Genode's block-session interfaces as both front and back end,
leading to the most common use case where this server will reside "between" a
block-driver server and a higher level component like a file-system server.
At startup, the partition server will try to parse the master boot record (MBR)
of its back-end block session. If no partition table is found, the whole block
device is exported as partition '0'. In the other case, the MBR and possible
extended boot records (EBRs) are parsed and offered as separate block sessions
to the front-end clients. The four primary partitions will receive partition
numbers '1' to '4' whereas the first logical partition will be assigned to '5'.
In order to route a client to the right partition, the server parses its
configuration section looking for 'policy' tags.
XML Syntax:
! <policy labal="<program name>" parition="<partition number>" />
Usage
-----
Configuration snippet with two clients and an (hypothetical) IDE driver:
!<start name="ata_driver">
! <resource name="RAM" quantum="1M" />
! <provides><service name="Block"/></provides>
! <config ata="yes" />
!</start>
!
!<start name="part_blk">
! <resource name="RAM" quantum="10M" />
! <provides><service name="Block" /></provides>
!
! <!-- route part_blk to the ata_driver -->
! <route>
! <any-service><child name="ata_driver"/> <parent/><any-child/></any-service>
! </route>
!
! <!-- allow program 'test-part1' to access logical partition '6', while program
! 'test-part2' receives access to primary partition 1 -->
! <config>
! <policy label="test-part1" partition="6"/>
! <policy label="test-part2" partition="1"/>
! </config>
!</start>
!
!<!-- part_blk clients -->
!<start name="test-part1">
! <binary name="test-part"/>
! <resource name="RAM" quantum="10M" />
! <route>
! <any-service> <child name="part_blk" /> <parent/> <any-child/> </any-service>
! </route>
!</start>
!
!<start name="test-part2">
! <binary name="test-part"/>
! <resource name="RAM" quantum="10M" />
! <route>
! <any-service> <child name="part_blk" /> <parent/> <any-child/> </any-service>
! </route>
!</start>

View File

@ -0,0 +1,292 @@
/*
* \brief Block-session component for partition server
* \author Stefan Kalkowski
* \date 2013-12-04
*/
/*
* Copyright (C) 2013 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 _PART_BLK__COMPONENT_H_
#define _PART_BLK__COMPONENT_H_
#include <base/exception.h>
#include <root/component.h>
#include <block_session/rpc_object.h>
#include "partition_table.h"
namespace Block {
using namespace Genode;
class Session_component;
class Root;
};
class Block::Session_component : public Block::Session_rpc_object,
public List<Block::Session_component>::Element,
public Block_dispatcher
{
private:
Ram_dataspace_capability _rq_ds;
addr_t _rq_phys;
Partition *_partition;
Signal_dispatcher<Session_component> _sink_ack;
Signal_dispatcher<Session_component> _sink_submit;
bool _req_queue_full;
bool _ack_queue_full;
Packet_descriptor _p_to_handle;
unsigned _p_in_fly;
/**
* Acknowledge a packet already handled
*/
inline void _ack_packet(Packet_descriptor &packet)
{
if (!tx_sink()->ready_to_ack())
PERR("Not ready to ack!");
tx_sink()->acknowledge_packet(packet);
_p_in_fly--;
}
/**
* Range check packet request
*/
inline bool _range_check(Packet_descriptor &p) {
return p.block_number() + p.block_count() <= _partition->sectors; }
/**
* Handle a single request
*/
void _handle_packet(Packet_descriptor packet)
{
_p_to_handle = packet;
_p_to_handle.succeeded(false);
/* ignore invalid packets */
if (!packet.valid() || !_range_check(_p_to_handle)) {
_ack_packet(_p_to_handle);
return;
}
bool write = _p_to_handle.operation() == Packet_descriptor::WRITE;
sector_t off = _p_to_handle.block_number() + _partition->lba;
size_t cnt = _p_to_handle.block_count();
void* addr = tx_sink()->packet_content(_p_to_handle);
try {
Driver::driver().io(write, off, cnt, addr, *this, _p_to_handle);
} catch (Block::Session::Tx::Source::Packet_alloc_failed) {
_req_queue_full = true;
Session_component::wait_queue().insert(this);
}
}
/**
* Triggered when a packet was placed into the empty submit queue
*/
void _packet_avail(unsigned)
{
_ack_queue_full = _p_in_fly >= tx_sink()->ack_slots_free();
/*
* as long as more packets are available, and we're able to ack
* them, and the driver's request queue isn't full,
* direct the packet request to the driver backend
*/
for (; !_req_queue_full && tx_sink()->packet_avail() &&
!_ack_queue_full; _p_in_fly++,
_ack_queue_full = _p_in_fly >= tx_sink()->ack_slots_free())
_handle_packet(tx_sink()->get_packet());
}
/**
* Triggered when an ack got removed from the full ack queue
*/
void _ready_to_ack(unsigned) { _packet_avail(0); }
public:
/**
* Constructor
*/
Session_component(Ram_dataspace_capability rq_ds,
Partition *partition,
Rpc_entrypoint &ep,
Signal_receiver &receiver)
: Session_rpc_object(rq_ds, ep),
_rq_ds(rq_ds),
_rq_phys(Dataspace_client(_rq_ds).phys_addr()),
_partition(partition),
_sink_ack(receiver, *this, &Session_component::_ready_to_ack),
_sink_submit(receiver, *this, &Session_component::_packet_avail),
_req_queue_full(false),
_ack_queue_full(false),
_p_in_fly(0)
{
_tx.sigh_ready_to_ack(_sink_ack);
_tx.sigh_packet_avail(_sink_submit);
}
Partition *partition() { return _partition; }
void dispatch(Packet_descriptor &request, Packet_descriptor &reply)
{
if (request.operation() == Block::Packet_descriptor::READ) {
void *src =
Driver::driver().session().tx()->packet_content(reply);
Genode::size_t sz =
request.block_count() * Driver::driver().blk_size();
Genode::memcpy(tx_sink()->packet_content(request), src, sz);
}
request.succeeded(true);
_ack_packet(request);
if (_ack_queue_full)
_packet_avail(0);
}
static List<Session_component>& wait_queue()
{
static List<Session_component> l;
return l;
}
static void wake_up()
{
for (Session_component *c = wait_queue().first(); c; c = c->next())
{
wait_queue().remove(c);
c->_req_queue_full = false;
c->_handle_packet(c->_p_to_handle);
c->_packet_avail(0);
}
}
/*******************************
** Block session interface **
*******************************/
void info(sector_t *blk_count, size_t *blk_size,
Operations *ops)
{
*blk_count = _partition->sectors;
*blk_size = Driver::driver().blk_size();
ops->set_operation(Packet_descriptor::READ);
ops->set_operation(Packet_descriptor::WRITE);
}
void sync() { Driver::driver().session().sync(); }
};
/**
* Root component, handling new session requests
*/
class Block::Root :
public Genode::Root_component<Block::Session_component>
{
private:
Rpc_entrypoint &_ep;
Signal_receiver &_receiver;
long _partition_num(const char *session_label)
{
long num = -1;
try {
using namespace Genode;
Xml_node policy = Genode::config()->xml_node().sub_node("policy");
for (;; policy = policy.next("policy")) {
char label_buf[64];
policy.attribute("label").value(label_buf, sizeof(label_buf));
if (Genode::strcmp(session_label, label_buf))
continue;
/* read partition attribute */
policy.attribute("partition").value(&num);
break;
}
} catch (...) {}
return num;
}
protected:
/**
* Always returns the singleton block-session component
*/
Session_component *_create_session(const char *args)
{
size_t ram_quota =
Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
size_t tx_buf_size =
Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
/* delete ram quota by the memory needed for the session */
size_t session_size = max((size_t)4096,
sizeof(Session_component)
+ sizeof(Allocator_avl));
if (ram_quota < session_size)
throw Root::Quota_exceeded();
/*
* Check if donated ram quota suffices for both
* communication buffers. Also check both sizes separately
* to handle a possible overflow of the sum of both sizes.
*/
if (tx_buf_size > ram_quota - session_size) {
PERR("insufficient 'ram_quota', got %zd, need %zd",
ram_quota, tx_buf_size + session_size);
throw Root::Quota_exceeded();
}
/* Search for configured partition number and the corresponding partition */
char label_buf[64];
Genode::Arg_string::find_arg(args,
"label").string(label_buf,
sizeof(label_buf),
"<unlabeled>");
long num = _partition_num(label_buf);
if (num < 0) {
PERR("No confguration found for client: %s", label_buf);
throw Root::Invalid_args();
}
if (!Partition_table::table().partition(num)) {
PERR("Partition %ld unavailable", num);
throw Root::Unavailable();
}
Ram_dataspace_capability ds_cap;
ds_cap = Genode::env()->ram_session()->alloc(tx_buf_size, true);
return new (md_alloc())
Session_component(ds_cap,
Partition_table::table().partition(num),
_ep, _receiver);
}
public:
Root(Rpc_entrypoint *session_ep, Allocator *md_alloc,
Signal_receiver &receiver)
:
Root_component(session_ep, md_alloc),
_ep(*session_ep),
_receiver(receiver)
{ }
};
#endif /* _PART_BLK__COMPONENT_H_ */

View File

@ -0,0 +1,155 @@
/*
* \brief Block-session driver for partition server
* \author Stefan Kalkowski
* \date 2013-12-04
*/
/*
* Copyright (C) 2013 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 _PART_BLK__DRIVER_H_
#define _PART_BLK__DRIVER_H_
#include <base/env.h>
#include <base/allocator_avl.h>
#include <base/signal.h>
#include <base/tslab.h>
#include <util/list.h>
#include <block_session/connection.h>
namespace Block {
class Block_dispatcher;
class Driver;
};
class Block::Block_dispatcher
{
public:
virtual void dispatch(Packet_descriptor&, Packet_descriptor&) = 0;
};
bool operator== (const Block::Packet_descriptor& p1,
const Block::Packet_descriptor& p2)
{
return p1.operation() == p2.operation() &&
p1.block_number() == p2.block_number() &&
p1.block_count() == p2.block_count();
}
class Block::Driver
{
public:
class Request : public Genode::List<Request>::Element
{
private:
Block_dispatcher &_dispatcher;
Packet_descriptor _cli;
Packet_descriptor _srv;
public:
Request(Block_dispatcher &d,
Packet_descriptor &cli,
Packet_descriptor &srv)
: _dispatcher(d), _cli(cli), _srv(srv) {}
bool handle(Packet_descriptor& reply)
{
bool ret = reply == _srv;
if (ret) _dispatcher.dispatch(_cli, reply);
return ret;
}
};
private:
enum { BLK_SZ = Session::TX_QUEUE_SIZE*sizeof(Request) };
Genode::Tslab<Request, BLK_SZ> _r_slab;
Genode::List<Request> _r_list;
Genode::Allocator_avl _block_alloc;
Block::Connection _session;
Block::sector_t _blk_cnt;
Genode::size_t _blk_size;
Genode::Signal_dispatcher<Driver> _source_ack;
Genode::Signal_dispatcher<Driver> _source_submit;
void _ready_to_submit(unsigned);
void _ack_avail(unsigned)
{
/* check for acknowledgements */
while (_session.tx()->ack_avail()) {
Packet_descriptor p = _session.tx()->get_acked_packet();
for (Request *r = _r_list.first(); r; r = r->next()) {
if (r->handle(p)) {
_r_list.remove(r);
Genode::destroy(&_r_slab, r);
break;
}
}
_session.tx()->release_packet(p);
}
_ready_to_submit(0);
}
public:
Driver(Genode::Signal_receiver &receiver)
: _r_slab(Genode::env()->heap()),
_block_alloc(Genode::env()->heap()),
_session(&_block_alloc, 4 * 1024 * 1024),
_source_ack(receiver, *this, &Driver::_ack_avail),
_source_submit(receiver, *this, &Driver::_ready_to_submit)
{
Block::Session::Operations ops;
_session.info(&_blk_cnt, &_blk_size, &ops);
}
Genode::size_t blk_size() { return _blk_size; }
Genode::size_t blk_cnt() { return _blk_cnt; }
Session_client& session() { return _session; }
void work_asynchronously()
{
_session.tx_channel()->sigh_ack_avail(_source_ack);
_session.tx_channel()->sigh_ready_to_submit(_source_submit);
}
static Driver& driver();
void io(bool write, sector_t nr, Genode::size_t cnt, void* addr,
Block_dispatcher &dispatcher, Packet_descriptor& cli)
{
if (!_session.tx()->ready_to_submit())
throw Block::Session::Tx::Source::Packet_alloc_failed();
Block::Packet_descriptor::Opcode op = write
? Block::Packet_descriptor::WRITE
: Block::Packet_descriptor::READ;
Genode::size_t size = _blk_size * cnt;
Packet_descriptor p(_session.dma_alloc_packet(size),
op, nr, cnt);
Request *r = new (&_r_slab) Request(dispatcher, cli, p);
_r_list.insert(r);
if (write)
Genode::memcpy(_session.tx()->packet_content(p),
addr, size);
_session.tx()->submit_packet(p);
}
};
#endif /* _PART_BLK__DRIVER_H_ */

View File

@ -0,0 +1,58 @@
/*
* \brief Front end of the partition server
* \author Sebastian Sumpf
* \author Stefan Kalkowski
* \date 2011-05-30
*/
/*
* Copyright (C) 2011-2013 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 <block_session/rpc_object.h>
#include <cap_session/connection.h>
#include <os/config.h>
#include "driver.h"
#include "partition_table.h"
#include "component.h"
static Genode::Signal_receiver receiver;
Block::Driver& Block::Driver::driver()
{
static Block::Driver driver(receiver);
return driver;
}
void Block::Driver::_ready_to_submit(unsigned) {
Block::Session_component::wake_up(); }
int main()
{
using namespace Genode;
if (!Block::Partition_table::table().avail()) {
PERR("No valid partition table found");
return 1;
}
enum { STACK_SIZE = 1024 * sizeof(Genode::size_t) };
static Cap_connection cap;
static Rpc_entrypoint ep(&cap, STACK_SIZE, "part_ep");
static Block::Root block_root(&ep, env()->heap(), receiver);
env()->parent()->announce(ep.manage(&block_root));
while (true) {
Signal s = receiver.wait_for_signal();
static_cast<Signal_dispatcher_base *>(s.context())->dispatch(s.num());
}
return 0;
}

View File

@ -0,0 +1,206 @@
/*
* \brief Partition table definitions
* \author Sebastian Sumpf
* \author Stefan Kalkowski
* \date 2013-12-04
*/
/*
* Copyright (C) 2013 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 _PART_BLK__PARTITION_TABLE_H_
#define _PART_BLK__PARTITION_TABLE_H_
#include <base/env.h>
#include <base/printf.h>
#include <block_session/client.h>
#include "driver.h"
namespace Block {
struct Partition;
class Partition_table;
}
struct Block::Partition
{
Genode::uint32_t lba; /* logical block address on device */
Genode::uint32_t sectors; /* number of sectors in patitions */
Partition(Genode::uint32_t l, Genode::uint32_t s)
: lba(l), sectors(s) { }
};
class Block::Partition_table
{
private:
class Sector
{
private:
Session_client &_session;
Packet_descriptor _p;
public:
Sector(unsigned long blk_nr,
unsigned long count,
bool write = false)
: _session(Driver::driver().session()),
_p(_session.dma_alloc_packet(Driver::driver().blk_size() * count),
write ? Packet_descriptor::WRITE : Packet_descriptor::READ,
blk_nr, count)
{
_session.tx()->submit_packet(_p);
_p = _session.tx()->get_acked_packet();
if (!_p.succeeded())
PERR("Could not access block %llu", _p.block_number());
}
~Sector() { _session.tx()->release_packet(_p); }
template <typename T> T addr() {
return reinterpret_cast<T>(_session.tx()->packet_content(_p)); }
};
/**
* Partition table entry format
*/
struct Partition_record
{
enum { INVALID = 0, EXTENTED = 0x5 };
Genode::uint8_t _unused[4];
Genode::uint8_t _type; /* partition type */
Genode::uint8_t _unused2[3];
Genode::uint32_t _lba; /* logical block address */
Genode::uint32_t _sectors; /* number of sectors */
bool is_valid() { return _type != INVALID; }
bool is_extented() { return _type == EXTENTED; }
} __attribute__((packed));
/**
* Master/Extented boot record format
*/
struct Mbr
{
Genode::uint8_t _unused[446];
Partition_record _records[4];
Genode::uint16_t _magic;
bool is_valid()
{
/* magic number of partition table */
enum { MAGIC = 0xaa55 };
return _magic == MAGIC;
}
} __attribute__((packed));
enum { MAX_PARTITIONS = 32 };
Partition *_part_list[MAX_PARTITIONS]; /* contains pointers to valid
partitions or 0 */
void _parse_extented(Partition_record *record)
{
Partition_record *r = record;
unsigned lba = r->_lba;
/* first logical partition number */
int nr = 5;
do {
Sector s(lba, 1);
Mbr *ebr = s.addr<Mbr *>();
if (!(ebr->is_valid()))
return;
/* The first record is the actual logical partition. The lba of this
* partition is relative to the lba of the current EBR */
Partition_record *logical = &(ebr->_records[0]);
if (logical->is_valid() && nr < MAX_PARTITIONS) {
_part_list[nr++] = new (Genode::env()->heap())
Partition(logical->_lba + lba, logical->_sectors);
PINF("Partition %d: LBA %u (%u blocks) type %x", nr - 1,
logical->_lba + lba, logical->_sectors, logical->_type);
}
/*
* the second record points to the next EBR
* (relative form this EBR)
*/
r = &(ebr->_records[1]);
lba += ebr->_records[1]._lba;
} while (r->is_valid());
}
void _parse_mbr(Mbr *mbr)
{
/* no partition table, use whole disc as partition 0 */
if (!(mbr->is_valid()))
_part_list[0] = new(Genode::env()->heap())
Partition(0, Driver::driver().blk_cnt() - 1);
for (int i = 0; i < 4; i++) {
Partition_record *r = &(mbr->_records[i]);
if (r->is_valid())
PINF("Partition %d: LBA %u (%u blocks) type: %x",
i + 1, r->_lba, r->_sectors, r->_type);
else
continue;
if (r->is_extented())
_parse_extented(r);
else
_part_list[i + 1] = new(Genode::env()->heap())
Partition(r->_lba, r->_sectors);
}
}
Partition_table()
{
Sector s(0, 1);
_parse_mbr(s.addr<Mbr *>());
/*
* we read all partition information,
* now it's safe to turn in asynchronous mode
*/
Driver::driver().work_asynchronously();
}
public:
Partition *partition(int num) {
return (num < MAX_PARTITIONS) ? _part_list[num] : 0; }
bool avail()
{
for (unsigned num = 0; num < MAX_PARTITIONS; num++)
if (_part_list[num])
return true;
return false;
}
static Partition_table& table()
{
static Partition_table table;
return table;
}
};
#endif /* _PART_BLK__PARTITION_TABLE_H_ */

View File

@ -0,0 +1,3 @@
TARGET = part_blk
LIBS = base config
SRC_CC = main.cc

View File

@ -0,0 +1,6 @@
RAM loop device is an implementation of the block-session interface
running as a client of a RAM session. It exports the requested ROM file as a
block device, similiar to loop devices known in Linux. The example shows how
to choose the right ROM file in its configuration and how to configure the exported block size.
! <config file="image.iso" block_size="2048"/>

View File

@ -0,0 +1,158 @@
/*
* \brief Provide a rom-file as writable block device (aka loop devices)
* \author Stefan Kalkowski
* \author Sebastian Sumpf
* \date 2010-07-07
*/
/*
* Copyright (C) 2010-2013 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 <base/exception.h>
#include <base/printf.h>
#include <os/attached_ram_dataspace.h>
#include <os/attached_rom_dataspace.h>
#include <os/config.h>
#include <os/server.h>
#include <rom_session/connection.h>
#include <block/component.h>
#include <block/driver.h>
using namespace Genode;
class Ram_blk : public Block::Driver
{
private:
Attached_rom_dataspace _rom_ds;
size_t _file_sz; /* file size */
size_t _blk_sz; /* block size */
size_t _blk_cnt; /* block count */
Attached_ram_dataspace _file_ds; /* copy of rom file */
addr_t _file_addr; /* start address of attached file */
void _io(Block::sector_t block_number,
size_t block_count,
char* buffer,
Block::Packet_descriptor &packet,
bool read)
{
/* sanity check block number */
if ((block_number + block_count > _file_sz / _blk_sz)
|| block_number < 0) {
PWRN("requested blocks %lld-%lld out of range!",
block_number, block_number + block_count);
return;
}
size_t offset = (size_t) block_number * _blk_sz;
size_t size = block_count * _blk_sz;
void *src = read ? (void *)(_file_addr + offset) : (void *)buffer;
void *dst = read ? (void *)buffer : (void *)(_file_addr + offset);
/* copy file content to packet payload */
memcpy(dst, src, size);
ack_packet(packet);
}
public:
Ram_blk(const char *name, size_t blk_sz)
: _rom_ds(name),
_file_sz(_rom_ds.size()),
_blk_sz(blk_sz),
_blk_cnt(_file_sz/_blk_sz),
_file_ds(env()->ram_session(), _file_sz),
_file_addr((addr_t)_file_ds.local_addr<addr_t>())
{
memcpy(_file_ds.local_addr<void>(), _rom_ds.local_addr<void>(), _file_sz);
}
/****************************
** Block-driver interface **
****************************/
Genode::size_t block_size() { return _blk_sz; }
Block::sector_t block_count() { return _blk_cnt; }
Block::Session::Operations ops()
{
Block::Session::Operations o;
o.set_operation(Block::Packet_descriptor::READ);
o.set_operation(Block::Packet_descriptor::WRITE);
return o;
}
void read(Block::sector_t block_number,
size_t block_count,
char* buffer,
Block::Packet_descriptor &packet)
{
_io(block_number, block_count, buffer, packet, true);
}
void write(Block::sector_t block_number,
Genode::size_t block_count,
const char * buffer,
Block::Packet_descriptor &packet)
{
_io(block_number, block_count, const_cast<char *>(buffer), packet, false);
}
};
struct Main
{
Server::Entrypoint &ep;
struct Factory : Block::Driver_factory
{
Block::Driver *create()
{
char file[64];
size_t blk_sz = 512;
try {
config()->xml_node().attribute("file").value(file, sizeof(file));
config()->xml_node().attribute("block_size").value(&blk_sz);
}
catch (...) { }
PINF("Using file=%s as device with block size %zx.", file, blk_sz);
try {
return new (Genode::env()->heap()) Ram_blk(file, blk_sz);
} catch(Rom_connection::Rom_connection_failed) {
PERR("Cannot open file %s.", file);
}
throw Root::Unavailable();
}
void destroy(Block::Driver *driver) {
Genode::destroy(env()->heap(), driver); }
} factory;
Block::Root root;
Main(Server::Entrypoint &ep)
: ep(ep), root(ep, Genode::env()->heap(), factory) {
Genode::env()->parent()->announce(ep.manage(root)); }
};
/************
** Server **
************/
namespace Server {
char const *name() { return "rom_blk_ep"; }
size_t stack_size() { return 2*1024*sizeof(long); }
void construct(Entrypoint &ep) { static Main server(ep); }
}

View File

@ -0,0 +1,3 @@
TARGET = ram_blk
SRC_CC = main.cc
LIBS = base config server

View File

@ -0,0 +1,57 @@
This directory contains an in-memory file-system implementation.
Configuration
~~~~~~~~~~~~~
Access to the file system can be tailored for each session depending on the
session's label. By default, no permissions are granted to any session.
To selectively permit access to (a part of) the file system, at least one
ram_fs policy must be defined.
The following configuration illustates the way of how to express policy.
! <config>
! <!-- preload RAM file system with some ROM images -->
! <content>
! <dir name="tmp">
! <rom name="init" as="blubb" />
! </dir>
! <dir name="home">
! <dir name="user">
! <inline name=".vimrc">
! set hidden
! </inline>
! </dir>
! </dir>
! </content>
! <!-- constrain sessions according to their labels -->
! <policy label="noux -> root" root="/" />
! <policy label="noux -> home" root="/home/user" writeable="yes" />
! <policy label="noux -> tmp" root="/tmp" writeable="yes" />
! </config>
The '<content>' sub node of the '<config>' node provides a way to pre-populate
the file system with directories and files. Note that '<dir>' nodes can be
arbitrarily nested. Files can be loaded from the ROM service. By adding the
optional 'at' attribute to a rom node, the file name can be defined
independently from the ROM module name. In addition to creating files from
ROM modules, files can be created from data specified directly as part of the
configuration using '<inline>' nodes. The content of such nodes is used as
file content as is.
Session-specific access-control policy is expressed via one or more '<policy>'
nodes. At session-creation time, each policy node is matched against the label
of the new session. If the label of a policy node matches, the defined policy
is applied. If multiple policies match, the one with the longest 'label'
attribute (the most specific one) is selected.
A policy node may contain the following attributes. The mandatory 'root'
attribute defines the viewport of the session onto the file system. The
optional 'writeable' attribute grants the permission to modify the file system.
Example
~~~~~~~
To illustrate the use of ram_fs, refer to the 'libports/run/libc_fs.run'
script.

View File

@ -0,0 +1,441 @@
/*
* \brief Data structure for storing sparse files in RAM
* \author Norman Feske
* \date 2012-04-18
*/
/*
* Copyright (C) 2012-2013 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 _CHUNK_H_
#define _CHUNK_H_
/* Genode includes */
#include <util/noncopyable.h>
#include <base/allocator.h>
#include <util/string.h>
#include <file_system_session/file_system_session.h>
namespace File_system {
using namespace Genode;
/**
* Common base class of both 'Chunk' and 'Chunk_index'
*/
class Chunk_base : Noncopyable
{
public:
class Index_out_of_range { };
protected:
seek_off_t const _base_offset;
size_t _num_entries; /* corresponds to last used entry */
/**
* Test if specified range lies within the chunk
*/
void assert_valid_range(seek_off_t start, size_t len,
file_size_t chunk_size) const
{
if (is_zero()) return;
if (start < _base_offset)
throw Index_out_of_range();
if (start + len > _base_offset + chunk_size)
throw Index_out_of_range();
}
Chunk_base(seek_off_t base_offset)
: _base_offset(base_offset), _num_entries(0) { }
/**
* Construct zero chunk
*
* A zero chunk is a chunk that cannot be written to. When reading
* from it, it returns zeros. Because there is a single zero chunk
* for each chunk type, the base offset is meaningless. We use a
* base offset of ~0 as marker to identify zero chunks.
*/
Chunk_base() : _base_offset(~0L), _num_entries(0) { }
public:
/**
* Return absolute base offset of chunk in bytes
*/
seek_off_t base_offset() const { return _base_offset; }
/**
* Return true if chunk is a read-only zero chunk
*/
bool is_zero() const { return _base_offset == (seek_off_t)(~0L); }
/**
* Return true if chunk has no allocated sub chunks
*/
bool empty() const { return _num_entries == 0; }
};
/**
* Chunk of bytes used as leaf in hierarchy of chunk indices
*/
template <unsigned CHUNK_SIZE>
class Chunk : public Chunk_base
{
private:
char _data[CHUNK_SIZE];
public:
enum { SIZE = CHUNK_SIZE };
/**
* Construct byte chunk
*
* \param base_offset absolute offset of chunk in bytes
*
* The first argument is unused. Its mere purpose is to make the
* signature of the constructor compatible to the constructor
* of 'Chunk_index'.
*/
Chunk(Allocator &, seek_off_t base_offset)
:
Chunk_base(base_offset)
{
memset(_data, 0, CHUNK_SIZE);
}
/**
* Construct zero chunk
*/
Chunk() { }
/**
* Return number of used entries
*
* The returned value corresponds to the index of the last used
* entry + 1. It does not correlate to the number of actually
* allocated entries (there may be ranges of zero blocks).
*/
file_size_t used_size() const { return _num_entries; }
void write(char const *src, size_t len, seek_off_t seek_offset)
{
assert_valid_range(seek_offset, len, SIZE);
/* offset relative to this chunk */
seek_off_t const local_offset = seek_offset - base_offset();
memcpy(&_data[local_offset], src, len);
_num_entries = max(_num_entries, local_offset + len);
}
void read(char *dst, size_t len, seek_off_t seek_offset) const
{
assert_valid_range(seek_offset, len, SIZE);
memcpy(dst, &_data[seek_offset - base_offset()], len);
}
void truncate(file_size_t size)
{
assert_valid_range(size, 0, SIZE);
/*
* Offset of the first free position (relative to the beginning
* this chunk).
*/
seek_off_t const local_offset = size - base_offset();
if (local_offset >= _num_entries)
return;
memset(&_data[local_offset], 0, _num_entries - local_offset);
_num_entries = local_offset;
}
};
template <unsigned NUM_ENTRIES, typename ENTRY_TYPE>
class Chunk_index : public Chunk_base
{
public:
typedef ENTRY_TYPE Entry;
enum { ENTRY_SIZE = ENTRY_TYPE::SIZE,
SIZE = ENTRY_SIZE*NUM_ENTRIES };
private:
Allocator &_alloc;
Entry * _entries[NUM_ENTRIES];
/**
* Return instance of a zero sub chunk
*/
static Entry const &_zero_chunk()
{
static Entry zero_chunk;
return zero_chunk;
}
/**
* Return sub chunk at given index
*
* If there is no sub chunk at the specified index, this function
* transparently allocates one. Hence, the returned sub chunk
* is ready to be written to.
*/
Entry &_entry_for_writing(unsigned index)
{
if (index >= NUM_ENTRIES)
throw Index_out_of_range();
if (_entries[index])
return *_entries[index];
seek_off_t entry_offset = base_offset() + index*ENTRY_SIZE;
_entries[index] = new (&_alloc) Entry(_alloc, entry_offset);
_num_entries = max(_num_entries, index + 1);
return *_entries[index];
}
/**
* Return sub chunk at given index (for reading only)
*
* This function transparently provides a zero sub chunk for any
* index that is not populated by a real chunk.
*/
Entry const &_entry_for_reading(unsigned index) const
{
if (index >= NUM_ENTRIES)
throw Index_out_of_range();
if (_entries[index])
return *_entries[index];
return _zero_chunk();
}
/**
* Return index of entry located at specified byte offset
*
* The caller of this function must make sure that the offset
* parameter is within the bounds of the chunk.
*/
unsigned _index_by_offset(seek_off_t offset) const
{
return (offset - base_offset()) / ENTRY_SIZE;
}
/**
* Apply operation 'func' to a range of entries
*/
template <typename THIS, typename DATA, typename FUNC>
static void _range_op(THIS &obj, DATA *data, size_t len,
seek_off_t seek_offset, FUNC const &func)
{
/*
* Depending on whether this function is called for reading
* (const function) or writing (non-const function), the
* operand type is const or non-const Entry. The correct type
* is embedded as a trait in the 'FUNC' functor type.
*/
typedef typename FUNC::Entry Const_qualified_entry;
obj.assert_valid_range(seek_offset, len, SIZE);
while (len > 0) {
unsigned const index = obj._index_by_offset(seek_offset);
Const_qualified_entry &entry = FUNC::lookup(obj, index);
/*
* Calculate byte offset relative to the chunk
*
* We cannot use 'entry.base_offset()' for this calculation
* because in the const case, the lookup might return a
* zero chunk, which has no defined base offset. Therefore,
* we calculate the base offset via index*ENTRY_SIZE.
*/
seek_off_t const local_seek_offset =
seek_offset - obj.base_offset() - index*ENTRY_SIZE;
/* available capacity at 'entry' starting at seek offset */
seek_off_t const capacity = ENTRY_SIZE - local_seek_offset;
seek_off_t const curr_len = min(len, capacity);
/* apply functor (read or write) to entry */
func(entry, data, curr_len, seek_offset);
/* advance to next entry */
len -= curr_len;
data += curr_len;
seek_offset += curr_len;
}
}
struct Write_func
{
typedef ENTRY_TYPE Entry;
static Entry &lookup(Chunk_index &chunk, unsigned i) {
return chunk._entry_for_writing(i); }
void operator () (Entry &entry, char const *src, size_t len,
seek_off_t seek_offset) const
{
entry.write(src, len, seek_offset);
}
};
struct Read_func
{
typedef ENTRY_TYPE const Entry;
static Entry &lookup(Chunk_index const &chunk, unsigned i) {
return chunk._entry_for_reading(i); }
void operator () (Entry &entry, char *dst, size_t len,
seek_off_t seek_offset) const
{
if (entry.is_zero())
memset(dst, 0, len);
else
entry.read(dst, len, seek_offset);
}
};
void _init_entries()
{
for (unsigned i = 0; i < NUM_ENTRIES; i++)
_entries[i] = 0;
}
void _destroy_entry(unsigned i)
{
if (_entries[i] && (i < _num_entries)) {
destroy(&_alloc, _entries[i]);
_entries[i] = 0;
}
}
public:
/**
* Constructor
*
* \param alloc allocator to use for allocating sub-chunk
* indices and chunks
* \param base_offset absolute offset of the chunk in bytes
*/
Chunk_index(Allocator &alloc, seek_off_t base_offset)
: Chunk_base(base_offset), _alloc(alloc) { _init_entries(); }
/**
* Construct zero chunk
*/
Chunk_index() : _alloc(*(Allocator *)0) { }
/**
* Destructor
*/
~Chunk_index()
{
for (unsigned i = 0; i < NUM_ENTRIES; i++)
_destroy_entry(i);
}
/**
* Return size of chunk in bytes
*
* The returned value corresponds to the position after the highest
* offset that was written to.
*/
file_size_t used_size() const
{
if (_num_entries == 0)
return 0;
/* size of entries that lie completely within the used range */
file_size_t const size_whole_entries = ENTRY_SIZE*(_num_entries - 1);
Entry *last_entry = _entries[_num_entries - 1];
if (!last_entry)
return size_whole_entries;
return size_whole_entries + last_entry->used_size();
}
/**
* Write data to chunk
*/
void write(char const *src, size_t len, seek_off_t seek_offset)
{
_range_op(*this, src, len, seek_offset, Write_func());
}
/**
* Read data from chunk
*/
void read(char *dst, size_t len, seek_off_t seek_offset) const
{
_range_op(*this, dst, len, seek_offset, Read_func());
}
/**
* Truncate chunk to specified size in bytes
*
* This function can be used to shrink a chunk only. Specifying a
* 'size' larger than 'used_size' has no effect. The value returned
* by 'used_size' refers always to the position of the last byte
* written to the chunk.
*/
void truncate(file_size_t size)
{
unsigned const trunc_index = _index_by_offset(size);
if (trunc_index >= _num_entries)
return;
for (unsigned i = trunc_index + 1; i < _num_entries; i++)
_destroy_entry(i);
/* traverse into sub chunks */
if (_entries[trunc_index])
_entries[trunc_index]->truncate(size);
_num_entries = trunc_index + 1;
/*
* If the truncated at a chunk boundary, we can release the
* empty trailing chunk at 'trunc_index'.
*/
if (_entries[trunc_index] && _entries[trunc_index]->empty()) {
_destroy_entry(trunc_index);
_num_entries--;
}
}
};
};
#endif /* _CHUNK_H_ */

View File

@ -0,0 +1,214 @@
/*
* \brief File-system directory node
* \author Norman Feske
* \date 2012-04-11
*/
#ifndef _DIRECTORY_H_
#define _DIRECTORY_H_
/* local includes */
#include <node.h>
#include <util.h>
#include <file.h>
#include <symlink.h>
namespace File_system {
class Directory : public Node
{
private:
List<Node> _entries;
size_t _num_entries;
public:
Directory(char const *name) : _num_entries(0) { Node::name(name); }
bool has_sub_node_unsynchronized(char const *name) const
{
Node const *sub_node = _entries.first();
for (; sub_node; sub_node = sub_node->next())
if (strcmp(sub_node->name(), name) == 0)
return true;
return false;
}
void adopt_unsynchronized(Node *node)
{
/*
* XXX inc ref counter
*/
_entries.insert(node);
_num_entries++;
mark_as_updated();
}
void discard_unsynchronized(Node *node)
{
_entries.remove(node);
_num_entries--;
mark_as_updated();
}
Node *lookup_and_lock(char const *path, bool return_parent = false)
{
if (strcmp(path, "") == 0) {
lock();
return this;
}
if (!path || path[0] == '/')
throw Lookup_failed();
/* find first path delimiter */
unsigned i = 0;
for (; path[i] && path[i] != '/'; i++);
/*
* If no path delimiter was found, we are the parent of the
* specified path.
*/
if (path[i] == 0 && return_parent) {
lock();
return this;
}
/*
* The offset 'i' corresponds to the end of the first path
* element, which can be either the end of the string or the
* first '/' character.
*/
/* try to find entry that matches the first path element */
Node *sub_node = _entries.first();
for (; sub_node; sub_node = sub_node->next())
if ((strlen(sub_node->name()) == i) &&
(strcmp(sub_node->name(), path, i) == 0))
break;
if (!sub_node)
throw Lookup_failed();
if (is_basename(path)) {
/*
* Because 'path' is a basename that corresponds to an
* existing sub_node, we have found what we were looking
* for.
*/
sub_node->lock();
return sub_node;
}
/*
* As 'path' contains one or more path delimiters, traverse
* into the sub directory names after the first path element.
*/
/*
* We cannot traverse into anything other than a directory.
*
* XXX we might follow symlinks here
*/
Directory *sub_dir = dynamic_cast<Directory *>(sub_node);
if (!sub_dir)
throw Lookup_failed();
return sub_dir->lookup_and_lock(path + i + 1, return_parent);
}
Directory *lookup_and_lock_dir(char const *path)
{
Node *node = lookup_and_lock(path);
Directory *dir = dynamic_cast<Directory *>(node);
if (dir)
return dir;
node->unlock();
throw Lookup_failed();
}
File *lookup_and_lock_file(char const *path)
{
Node *node = lookup_and_lock(path);
File *file = dynamic_cast<File *>(node);
if (file)
return file;
node->unlock();
throw Lookup_failed();
}
Symlink *lookup_and_lock_symlink(char const *path)
{
Node *node = lookup_and_lock(path);
Symlink *symlink = dynamic_cast<Symlink *>(node);
if (symlink)
return symlink;
node->unlock();
throw Lookup_failed();
}
/**
* Lookup parent directory of the specified path
*
* \throw Lookup_failed
*/
Directory *lookup_and_lock_parent(char const *path)
{
return static_cast<Directory *>(lookup_and_lock(path, true));
}
size_t read(char *dst, size_t len, seek_off_t seek_offset)
{
if (len < sizeof(Directory_entry)) {
PERR("read buffer too small for directory entry");
return 0;
}
seek_off_t index = seek_offset / sizeof(Directory_entry);
if (seek_offset % sizeof(Directory_entry)) {
PERR("seek offset not alighed to sizeof(Directory_entry)");
return 0;
}
/* find list element */
Node *node = _entries.first();
for (unsigned i = 0; i < index && node; node = node->next(), i++);
/* index out of range */
if (!node)
return 0;
Directory_entry *e = (Directory_entry *)(dst);
if (dynamic_cast<File *>(node)) e->type = Directory_entry::TYPE_FILE;
if (dynamic_cast<Directory *>(node)) e->type = Directory_entry::TYPE_DIRECTORY;
if (dynamic_cast<Symlink *>(node)) e->type = Directory_entry::TYPE_SYMLINK;
strncpy(e->name, node->name(), sizeof(e->name));
return sizeof(Directory_entry);
}
size_t write(char const *src, size_t len, seek_off_t seek_offset)
{
/* writing to directory nodes is not supported */
return 0;
}
size_t num_entries() const { return _num_entries; }
};
}
#endif /* _DIRECTORY_H_ */

Some files were not shown because too many files have changed in this diff Show More