mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-19 07:38:28 +00:00
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:
569
repos/os/src/server/blk_cache/chunk.h
Normal file
569
repos/os/src/server/blk_cache/chunk.h
Normal 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_ */
|
433
repos/os/src/server/blk_cache/driver.h
Normal file
433
repos/os/src/server/blk_cache/driver.h
Normal 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(); }
|
||||
};
|
62
repos/os/src/server/blk_cache/lru.cc
Normal file
62
repos/os/src/server/blk_cache/lru.cc
Normal 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();
|
||||
}
|
25
repos/os/src/server/blk_cache/lru.h
Normal file
25
repos/os/src/server/blk_cache/lru.h
Normal 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);
|
||||
};
|
84
repos/os/src/server/blk_cache/main.cc
Normal file
84
repos/os/src/server/blk_cache/main.cc
Normal 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); }
|
||||
}
|
3
repos/os/src/server/blk_cache/target.mk
Normal file
3
repos/os/src/server/blk_cache/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = blk_cache
|
||||
LIBS = base server
|
||||
SRC_CC = main.cc lru.cc
|
19
repos/os/src/server/dynamic_rom/README
Normal file
19
repos/os/src/server/dynamic_rom/README
Normal 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.
|
288
repos/os/src/server/dynamic_rom/main.cc
Normal file
288
repos/os/src/server/dynamic_rom/main.cc
Normal 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);
|
||||
}
|
||||
}
|
3
repos/os/src/server/dynamic_rom/target.mk
Normal file
3
repos/os/src/server/dynamic_rom/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = dynamic_rom
|
||||
SRC_CC = main.cc
|
||||
LIBS = base server config
|
16
repos/os/src/server/fs_rom/README
Normal file
16
repos/os/src/server/fs_rom/README
Normal 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.
|
470
repos/os/src/server/fs_rom/main.cc
Executable file
470
repos/os/src/server/fs_rom/main.cc
Executable 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;
|
||||
}
|
3
repos/os/src/server/fs_rom/target.mk
Executable file
3
repos/os/src/server/fs_rom/target.mk
Executable file
@ -0,0 +1,3 @@
|
||||
TARGET = fs_rom
|
||||
SRC_CC = main.cc
|
||||
LIBS = base
|
27
repos/os/src/server/iso9660/README
Normal file
27
repos/os/src/server/iso9660/README
Normal 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.
|
257
repos/os/src/server/iso9660/backing_store.h
Normal file
257
repos/os/src/server/iso9660/backing_store.h
Normal 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_ */
|
496
repos/os/src/server/iso9660/iso9660.cc
Normal file
496
repos/os/src/server/iso9660/iso9660.cc
Normal 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 */
|
81
repos/os/src/server/iso9660/iso9660.h
Normal file
81
repos/os/src/server/iso9660/iso9660.h
Normal 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);
|
||||
}
|
320
repos/os/src/server/iso9660/main.cc
Normal file
320
repos/os/src/server/iso9660/main.cc
Normal 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;
|
||||
}
|
||||
|
||||
|
3
repos/os/src/server/iso9660/target.mk
Normal file
3
repos/os/src/server/iso9660/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = iso9660
|
||||
SRC_CC = main.cc iso9660.cc
|
||||
LIBS = base
|
5
repos/os/src/server/loader/README
Normal file
5
repos/os/src/server/loader/README
Normal 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.
|
179
repos/os/src/server/loader/child.h
Normal file
179
repos/os/src/server/loader/child.h
Normal 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_ */
|
103
repos/os/src/server/loader/input.h
Normal file
103
repos/os/src/server/loader/input.h
Normal 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_ */
|
429
repos/os/src/server/loader/main.cc
Normal file
429
repos/os/src/server/loader/main.cc
Normal 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;
|
||||
}
|
310
repos/os/src/server/loader/nitpicker.h
Normal file
310
repos/os/src/server/loader/nitpicker.h
Normal 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_ */
|
95
repos/os/src/server/loader/ram_session_client_guard.h
Normal file
95
repos/os/src/server/loader/ram_session_client_guard.h
Normal 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_ */
|
244
repos/os/src/server/loader/rom.h
Normal file
244
repos/os/src/server/loader/rom.h
Normal 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_ */
|
4
repos/os/src/server/loader/target.mk
Normal file
4
repos/os/src/server/loader/target.mk
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = loader
|
||||
LIBS = base init_pd_args
|
||||
SRC_CC = main.cc
|
||||
INC_DIR += $(PRG_DIR)
|
128
repos/os/src/server/log_report/main.cc
Normal file
128
repos/os/src/server/log_report/main.cc
Normal 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);
|
||||
}
|
||||
}
|
3
repos/os/src/server/log_report/target.mk
Normal file
3
repos/os/src/server/log_report/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = log_report
|
||||
SRC_CC = main.cc
|
||||
LIBS = base server
|
195
repos/os/src/server/log_terminal/main.cc
Normal file
195
repos/os/src/server/log_terminal/main.cc
Normal 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);
|
||||
}
|
||||
}
|
3
repos/os/src/server/log_terminal/target.mk
Normal file
3
repos/os/src/server/log_terminal/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = log_terminal
|
||||
SRC_CC = main.cc
|
||||
LIBS = base server
|
44
repos/os/src/server/lx_fs/README
Normal file
44
repos/os/src/server/lx_fs/README
Normal 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.
|
188
repos/os/src/server/lx_fs/directory.h
Normal file
188
repos/os/src/server/lx_fs/directory.h
Normal 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_ */
|
138
repos/os/src/server/lx_fs/file.h
Normal file
138
repos/os/src/server/lx_fs/file.h
Normal 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_ */
|
39
repos/os/src/server/lx_fs/lx_util.h
Normal file
39
repos/os/src/server/lx_fs/lx_util.h
Normal 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
|
441
repos/os/src/server/lx_fs/main.cc
Normal file
441
repos/os/src/server/lx_fs/main.cc
Normal 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); }
|
134
repos/os/src/server/lx_fs/node.h
Normal file
134
repos/os/src/server/lx_fs/node.h
Normal 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_ */
|
198
repos/os/src/server/lx_fs/node_handle_registry.h
Normal file
198
repos/os/src/server/lx_fs/node_handle_registry.h
Normal 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_ */
|
50
repos/os/src/server/lx_fs/symlink.h
Normal file
50
repos/os/src/server/lx_fs/symlink.h
Normal 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_ */
|
6
repos/os/src/server/lx_fs/target.mk
Normal file
6
repos/os/src/server/lx_fs/target.mk
Normal 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
|
63
repos/os/src/server/lx_fs/util.h
Normal file
63
repos/os/src/server/lx_fs/util.h
Normal 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_ */
|
415
repos/os/src/server/mixer/mixer.cc
Normal file
415
repos/os/src/server/mixer/mixer.cc
Normal 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;
|
||||
}
|
||||
|
3
repos/os/src/server/mixer/target.mk
Normal file
3
repos/os/src/server/mixer/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = mixer
|
||||
SRC_CC = mixer.cc
|
||||
LIBS = base
|
43
repos/os/src/server/nic_bridge/README
Normal file
43
repos/os/src/server/nic_bridge/README
Normal 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>
|
103
repos/os/src/server/nic_bridge/address_node.h
Normal file
103
repos/os/src/server/nic_bridge/address_node.h
Normal 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_ */
|
47
repos/os/src/server/nic_bridge/avl_safe.h
Normal file
47
repos/os/src/server/nic_bridge/avl_safe.h
Normal 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_ */
|
160
repos/os/src/server/nic_bridge/component.cc
Normal file
160
repos/os/src/server/nic_bridge/component.cc
Normal 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();
|
||||
}
|
242
repos/os/src/server/nic_bridge/component.h
Normal file
242
repos/os/src/server/nic_bridge/component.h
Normal 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_ */
|
36
repos/os/src/server/nic_bridge/env.cc
Normal file
36
repos/os/src/server/nic_bridge/env.cc
Normal 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;
|
||||
}
|
36
repos/os/src/server/nic_bridge/env.h
Normal file
36
repos/os/src/server/nic_bridge/env.h
Normal 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_ */
|
45
repos/os/src/server/nic_bridge/list_safe.h
Normal file
45
repos/os/src/server/nic_bridge/list_safe.h
Normal 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_ */
|
21
repos/os/src/server/nic_bridge/mac.cc
Normal file
21
repos/os/src/server/nic_bridge/mac.cc
Normal 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);
|
80
repos/os/src/server/nic_bridge/mac.h
Normal file
80
repos/os/src/server/nic_bridge/mac.h
Normal 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_ */
|
64
repos/os/src/server/nic_bridge/main.cc
Normal file
64
repos/os/src/server/nic_bridge/main.cc
Normal 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;
|
||||
}
|
137
repos/os/src/server/nic_bridge/nic.cc
Normal file
137
repos/os/src/server/nic_bridge/nic.cc
Normal 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);
|
||||
}
|
64
repos/os/src/server/nic_bridge/nic.h
Normal file
64
repos/os/src/server/nic_bridge/nic.h
Normal 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_ */
|
125
repos/os/src/server/nic_bridge/packet_handler.cc
Normal file
125
repos/os/src/server/nic_bridge/packet_handler.cc
Normal 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)
|
||||
{ }
|
132
repos/os/src/server/nic_bridge/packet_handler.h
Normal file
132
repos/os/src/server/nic_bridge/packet_handler.h
Normal 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_ */
|
5
repos/os/src/server/nic_bridge/target.mk
Normal file
5
repos/os/src/server/nic_bridge/target.mk
Normal 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
|
53
repos/os/src/server/nic_bridge/vlan.h
Normal file
53
repos/os/src/server/nic_bridge/vlan.h
Normal 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_ */
|
211
repos/os/src/server/nic_loopback/main.cc
Normal file
211
repos/os/src/server/nic_loopback/main.cc
Normal 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;
|
||||
}
|
||||
|
3
repos/os/src/server/nic_loopback/target.mk
Normal file
3
repos/os/src/server/nic_loopback/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = nic_loopback
|
||||
SRC_CC = main.cc
|
||||
LIBS = base
|
11
repos/os/src/server/nit_fb/README
Normal file
11
repos/os/src/server/nit_fb/README
Normal 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.
|
249
repos/os/src/server/nit_fb/main.cc
Normal file
249
repos/os/src/server/nit_fb/main.cc
Normal 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;
|
||||
}
|
||||
|
3
repos/os/src/server/nit_fb/target.mk
Normal file
3
repos/os/src/server/nit_fb/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = nit_fb
|
||||
SRC_CC = main.cc
|
||||
LIBS = base config
|
74
repos/os/src/server/nitpicker/README
Normal file
74
repos/os/src/server/nitpicker/README
Normal 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 Nitpicker’s 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" />
|
||||
|
63
repos/os/src/server/nitpicker/background.h
Normal file
63
repos/os/src/server/nitpicker/background.h
Normal 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_ */
|
36
repos/os/src/server/nitpicker/big_mouse.h
Normal file
36
repos/os/src/server/nitpicker/big_mouse.h
Normal 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},
|
||||
}
|
||||
};
|
102
repos/os/src/server/nitpicker/canvas.h
Normal file
102
repos/os/src/server/nitpicker/canvas.h
Normal 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_ */
|
110
repos/os/src/server/nitpicker/chunky_menubar.h
Normal file
110
repos/os/src/server/nitpicker/chunky_menubar.h
Normal 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
|
54
repos/os/src/server/nitpicker/clip_guard.h
Normal file
54
repos/os/src/server/nitpicker/clip_guard.h
Normal 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_ */
|
27
repos/os/src/server/nitpicker/color.h
Normal file
27
repos/os/src/server/nitpicker/color.h
Normal 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_ */
|
BIN
repos/os/src/server/nitpicker/default.tff
Executable file
BIN
repos/os/src/server/nitpicker/default.tff
Executable file
Binary file not shown.
72
repos/os/src/server/nitpicker/draw_label.h
Normal file
72
repos/os/src/server/nitpicker/draw_label.h
Normal 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_ */
|
90
repos/os/src/server/nitpicker/global_keys.cc
Normal file
90
repos/os/src/server/nitpicker/global_keys.cc
Normal 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) { }
|
||||
}
|
||||
|
96
repos/os/src/server/nitpicker/global_keys.h
Normal file
96
repos/os/src/server/nitpicker/global_keys.h
Normal 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_ */
|
98
repos/os/src/server/nitpicker/input.h
Normal file
98
repos/os/src/server/nitpicker/input.h
Normal 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_ */
|
1085
repos/os/src/server/nitpicker/main.cc
Normal file
1085
repos/os/src/server/nitpicker/main.cc
Normal file
File diff suppressed because it is too large
Load Diff
51
repos/os/src/server/nitpicker/menubar.h
Normal file
51
repos/os/src/server/nitpicker/menubar.h
Normal 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
|
61
repos/os/src/server/nitpicker/mode.h
Normal file
61
repos/os/src/server/nitpicker/mode.h
Normal 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
|
80
repos/os/src/server/nitpicker/mouse_cursor.h
Normal file
80
repos/os/src/server/nitpicker/mouse_cursor.h
Normal 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
|
141
repos/os/src/server/nitpicker/session.h
Normal file
141
repos/os/src/server/nitpicker/session.h
Normal 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
|
9
repos/os/src/server/nitpicker/target.mk
Normal file
9
repos/os/src/server/nitpicker/target.mk
Normal 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
|
294
repos/os/src/server/nitpicker/user_state.cc
Normal file
294
repos/os/src/server/nitpicker/user_state.cc
Normal 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);
|
||||
}
|
||||
|
95
repos/os/src/server/nitpicker/user_state.h
Normal file
95
repos/os/src/server/nitpicker/user_state.h
Normal 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
|
129
repos/os/src/server/nitpicker/view.cc
Normal file
129
repos/os/src/server/nitpicker/view.cc
Normal 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);
|
||||
}
|
217
repos/os/src/server/nitpicker/view.h
Normal file
217
repos/os/src/server/nitpicker/view.h
Normal 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_ */
|
323
repos/os/src/server/nitpicker/view_stack.cc
Normal file
323
repos/os/src/server/nitpicker/view_stack.cc
Normal 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);
|
||||
}
|
222
repos/os/src/server/nitpicker/view_stack.h
Normal file
222
repos/os/src/server/nitpicker/view_stack.h
Normal 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
|
65
repos/os/src/server/part_blk/README
Normal file
65
repos/os/src/server/part_blk/README
Normal 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>
|
292
repos/os/src/server/part_blk/component.h
Normal file
292
repos/os/src/server/part_blk/component.h
Normal 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_ */
|
155
repos/os/src/server/part_blk/driver.h
Normal file
155
repos/os/src/server/part_blk/driver.h
Normal 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_ */
|
58
repos/os/src/server/part_blk/main.cc
Normal file
58
repos/os/src/server/part_blk/main.cc
Normal 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;
|
||||
}
|
206
repos/os/src/server/part_blk/partition_table.h
Normal file
206
repos/os/src/server/part_blk/partition_table.h
Normal 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_ */
|
3
repos/os/src/server/part_blk/target.mk
Normal file
3
repos/os/src/server/part_blk/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = part_blk
|
||||
LIBS = base config
|
||||
SRC_CC = main.cc
|
6
repos/os/src/server/ram_blk/README
Normal file
6
repos/os/src/server/ram_blk/README
Normal 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"/>
|
158
repos/os/src/server/ram_blk/main.cc
Normal file
158
repos/os/src/server/ram_blk/main.cc
Normal 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); }
|
||||
}
|
||||
|
3
repos/os/src/server/ram_blk/target.mk
Normal file
3
repos/os/src/server/ram_blk/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = ram_blk
|
||||
SRC_CC = main.cc
|
||||
LIBS = base config server
|
57
repos/os/src/server/ram_fs/README
Normal file
57
repos/os/src/server/ram_fs/README
Normal 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.
|
441
repos/os/src/server/ram_fs/chunk.h
Normal file
441
repos/os/src/server/ram_fs/chunk.h
Normal 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_ */
|
214
repos/os/src/server/ram_fs/directory.h
Normal file
214
repos/os/src/server/ram_fs/directory.h
Normal 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
Reference in New Issue
Block a user