part_blk: re-design to work event driven

Fix #750
This commit is contained in:
Stefan Kalkowski 2013-12-05 15:02:44 +01:00 committed by Norman Feske
parent 35bb156972
commit af86e33c3f
11 changed files with 708 additions and 749 deletions

View File

@ -2,48 +2,40 @@
# \brief Test of Block session interface provided by server/part_blk
#
if {![have_spec x86] || ![is_qemu_available]} {
puts "Run script is only supported on x86 and Qemu"; exit 0 }
set block_count 20480
if { [file exists ata.raw] == 0 } then {
if { [file exists bin/ata.raw] == 0 } then {
# create empty block device file
catch { exec dd if=/dev/zero of=ata.raw bs=512 count=$block_count }
# create to tro primary partitions (one is extented) and two logical paritions
catch { exec dd if=/dev/zero of=bin/ata.raw bs=512 count=$block_count }
if [catch { set sfdisk [ exec which sfdisk ] }] {
puts "sfdisk needs to be installed!"
exit 1
}
# create two primary partitions (one is extented) and two logical paritions
puts "using sfdisk to partition disk image, requires root privileges"
catch { exec echo "2048,4096,c\n4096,16386,5\n0,0\n0,0\n6144,4096,c\n12288,8192,c\n" | sudo sfdisk -uS -f ata.raw }
catch { exec echo "2048,4096,c\n4096,16386,5\n0,0\n0,0\n6144,4096,c\n12288,8192,c\n" | $sfdisk -uS -f bin/ata.raw }
}
set use_sd_card_drv [expr [have_spec omap4] || [have_spec exynos5] || [have_spec pl180]]
set use_atapi_drv [have_spec x86]
#
# Build
#
set build_components {
build {
core init
drivers/timer
server/rom_blk
server/part_blk
test/part_blk
test/blk/cli
}
lappend_if [have_spec pci] build_components drivers/pci
lappend_if [have_spec acpi] build_components drivers/acpi
lappend_if $use_atapi_drv build_components drivers/atapi
lappend_if $use_sd_card_drv build_components drivers/sd_card
build $build_components
create_boot_directory
#
# Generate config
#
append config {
install_config {
<config prio_levels="1" verbose="yes">
<parent-provides>
<service name="ROM"/>
@ -65,11 +57,16 @@ append config {
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="rom_blk">
<resource name="RAM" quantum="32M"/>
<provides><service name="Block"/></provides>
<config file="ata.raw" block_size="512"/>
</start>
<start name="part_blk">
<resource name="RAM" quantum="10M" />
<provides><service name="Block" /></provides>
<route>
<any-service><child name="blk_drv"/> <parent/><any-child/></any-service>
<any-service><child name="rom_blk"/> <parent/><any-child/></any-service>
</route>
<config>
<policy label="test-part1" partition="6"/>
@ -77,102 +74,33 @@ append config {
</config>
</start>
<start name="test-part1">
<binary name="test-part"/>
<resource name="RAM" quantum="10M" />
<binary name="test-blk-cli"/>
<resource name="RAM" quantum="5M" />
<route>
<any-service> <child name="part_blk" /> <parent/> <any-child/> </any-service>
</route>
<config pattern="0x44" />
</start>
<start name="test-part2">
<binary name="test-part"/>
<resource name="RAM" quantum="10M" />
<binary name="test-blk-cli"/>
<resource name="RAM" quantum="5M" />
<route>
<any-service> <child name="part_blk" /> <parent/> <any-child/> </any-service>
</route>
<config pattern="0x33" />
</start>
}
append_if [have_spec acpi] config {
<start name="acpi">
<resource name="RAM" quantum="8M"/>
<binary name="acpi_drv"/>
<provides>
<service name="PCI"/>
<service name="IRQ" />
</provides>
<route>
<service name="PCI"> <any-child /> </service>
<any-service> <parent/> <any-child /> </any-service>
</route>
</start>}
append_if [expr ![have_spec acpi] && [have_spec pci]] config {
<start name="pci_drv">
<resource name="RAM" quantum="5M"/>
<provides><service name="PCI"/></provides>
</start>}
append_if $use_atapi_drv config {
<start name="blk_drv">
<binary name="atapi_drv"/>
<resource name="RAM" quantum="1M"/>
<provides> <service name="Block"/> </provides>
<config ata="yes" />
</start>
}
append_if $use_sd_card_drv config {
<start name="blk_drv">
<binary name="sd_card_drv"/>
<resource name="RAM" quantum="1M" />
<provides><service name="Block"/></provides>
</start>
}
append config {
</config> }
install_config $config
#
# Boot modules
#
set boot_modules {
core init timer part_blk test-part
}
lappend_if [have_spec pci] boot_modules pci_drv
lappend_if [have_spec acpi] boot_modules acpi_drv
lappend_if $use_atapi_drv boot_modules atapi_drv
lappend_if $use_sd_card_drv boot_modules sd_card_drv
build_boot_image $boot_modules
build_boot_image { core init timer rom_blk part_blk test-blk-cli ata.raw }
#
# Qemu
#
append qemu_args " -nographic -m 64 "
append_if $use_atapi_drv qemu_args " -boot d -hda ata.raw "
append_if $use_sd_card_drv qemu_args " -drive file=ata.raw,if=sd "
append qemu_args " -nographic -m 128 "
run_genode_until "Success.*\n.*Success.*\n" 10
# Check whether atapi_drv reports the right start and end sectors
set sector_range [regexp -inline {First block: [0-9]+ last block [0-9]+} $output]
set sector_range [regexp -all -inline {[0-9]+} $sector_range]
if {[lindex $sector_range 0] != 0 || [lindex $sector_range 1] != [expr $block_count - 1]} {
puts "Error: block range mismatch, expected \[0-$block_count), got \[[lindex $sector_range 0]-[lindex $sector_range 1])"
exit 1
}
grep_output {^\[init -> test-part}
unify_output {[0-9]} "x"
compare_output_to {
[init -> test-partx] Success
[init -> test-partx] Success
}
run_genode_until "Tests finished successfully.*\n.*Tests finished successfully.*\n" 50
exec rm bin/ata.raw
puts "Test succeeded"

View File

@ -1,244 +0,0 @@
/*
* \brief Back end to other block interface
* \author Sebsastian Sumpf
* \date 2011-05-24
*/
/*
* 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 <base/allocator_avl.h>
#include <base/semaphore.h>
#include <block_session/connection.h>
#include "part_blk.h"
using namespace Genode;
/**
* Used to block until a packet has been freed
*/
static Semaphore _alloc_sem(0);
namespace Partition {
size_t _blk_cnt;
size_t _blk_size;
Allocator_avl _block_alloc(env()->heap());
Block::Connection _blk(&_block_alloc, 4 * MAX_PACKET_SIZE);
Partition *_part_list[MAX_PARTITIONS]; /* contains pointers to valid partittions or 0 */
Partition *partition(int num) { return (num < MAX_PARTITIONS) ? _part_list[num] : 0; }
size_t blk_size() { return _blk_size; }
inline unsigned long max_packets() { return (MAX_PACKET_SIZE / _blk_size); }
void sync() { _blk.sync(); }
/**
* Partition table entry format
*/
struct Partition_record
{
enum { INVALID = 0, EXTENTED = 0x5 };
uint8_t _unused[4];
uint8_t _type; /* partition type */
uint8_t _unused2[3];
uint32_t _lba; /* logical block address */
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
{
uint8_t _unused[446];
Partition_record _records[4];
uint16_t _magic;
bool is_valid()
{
/* magic number of partition table */
enum { MAGIC = 0xaa55 };
return _magic == MAGIC;
}
} __attribute__((packed));
class Sector
{
private:
Block::Packet_descriptor _p;
protected:
static Lock _lock;
public:
Sector(unsigned long blk_nr, unsigned long count, bool write = false)
{
using namespace Block;
Lock::Guard guard(_lock);
Block::Packet_descriptor::Opcode op = write ? Block::Packet_descriptor::WRITE
: Block::Packet_descriptor::READ;
_p = Block::Packet_descriptor(_blk.dma_alloc_packet(_blk_size * count),
op, blk_nr, count);
}
void submit_request()
{
Lock::Guard guard(_lock);
_blk.tx()->submit_packet(_p);
_p = _blk.tx()->get_acked_packet();
/* unblock clients that possibly wait for packet stream allocations */
if (_alloc_sem.cnt() < 0)
_alloc_sem.up();
if (!_p.succeeded()) {
PERR("Could not access block %llu", _p.block_number());
throw Io_error();
}
}
~Sector()
{
Lock::Guard guard(_lock);
_blk.tx()->release_packet(_p);
}
template <typename T>
T addr() { return reinterpret_cast<T>(_blk.tx()->packet_content(_p)); }
};
Lock Sector::_lock;
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);
s.submit_request();
Mbr *ebr = s.addr<Mbr *>();
if (!(ebr->is_valid()))
throw Io_error();
/* 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 (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(env()->heap())Partition(0, _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(env()->heap()) Partition(r->_lba, r->_sectors);
}
}
void init()
{
Block::Session::Operations ops;
/* device info */
_blk.info(&_blk_cnt, &_blk_size, &ops);
/* read MBR */
Sector s(0, 1);
s.submit_request();
parse_mbr(s.addr<Mbr *>());
}
void _io(unsigned long lba, unsigned long count, uint8_t *buf, bool write)
{
unsigned long bytes;
while (count) {
unsigned long curr_count = min<unsigned long>(count, max_packets());
bytes = curr_count * _blk_size;
bool alloc = false;
while (!alloc) {
try {
Sector sec(lba, curr_count, write);
if (!write) {
sec.submit_request();
memcpy(buf, sec.addr<void *>(), bytes);
}
else {
memcpy(sec.addr<void *>(), buf, bytes);
sec.submit_request();
}
alloc = true;
} catch (Block::Session::Tx::Source::Packet_alloc_failed) {
/* block */
_alloc_sem.down();
}
}
lba += curr_count;
count -= curr_count;
buf += bytes;
}
/* zero out rest of page */
bytes = (count * _blk_size) % 4096;
if (bytes)
memset(buf, 0, bytes);
}
void Partition::io(unsigned long block_nr, unsigned long count, void *buf, bool write)
{
if (block_nr + count > _sectors)
throw Io_error();
_io(_lba + block_nr, count, (uint8_t *)buf, write);
}
}

View File

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

View File

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

View File

@ -1,6 +1,7 @@
/*
* \brief Front end of the partition server
* \author Sebastian Sumpf
* \author Stefan Kalkowski
* \date 2011-05-30
*/
@ -11,227 +12,47 @@
* under the terms of the GNU General Public License version 2.
*/
#include <base/sleep.h>
#include <block_session/rpc_object.h>
#include <cap_session/connection.h>
#include <os/config.h>
#include <root/component.h>
#include "part_blk.h"
namespace Block {
#include "driver.h"
#include "partition_table.h"
#include "component.h"
long partition_num(const char *session_label)
{
long num = -1;
static Genode::Signal_receiver receiver;
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;
}
class Session_component : public Session_rpc_object
{
private:
class Tx_thread : public Genode::Thread<8192>
{
private:
Session_component *_session;
public:
Tx_thread(Session_component *session)
: Thread("block_session_tx"), _session(session) { }
void entry()
{
using namespace Genode;
Session_component::Tx::Sink *tx_sink = _session->tx_sink();
Block::Packet_descriptor packet;
/* handle requests */
while (true) {
/* blocking get packet from client */
packet = tx_sink->get_packet();
if (!packet.valid()) {
PWRN("received invalid packet");
continue;
}
packet.succeeded(false);
bool write = false;
switch (packet.operation()) {
case Block::Packet_descriptor::WRITE:
write = true;
case Block::Packet_descriptor::READ:
try {
_session->partition()->io(packet.block_number(),
packet.block_count(),
tx_sink->packet_content(packet),
write);
packet.succeeded(true);
}
catch (Partition::Io_error) {
PWRN("Io error!");
}
break;
default:
PWRN("received invalid packet");
continue;
}
/* acknowledge packet to the client */
if (!tx_sink->ready_to_ack())
PDBG("need to wait until ready-for-ack");
tx_sink->acknowledge_packet(packet);
}
}
};
struct Partition::Partition *_partition; /* partition belonging to this session */
Genode::Dataspace_capability _tx_ds; /* buffer for tx channel */
Tx_thread _tx_thread;
public:
Session_component(Genode::Dataspace_capability tx_ds,
Partition::Partition *partition,
Genode::Rpc_entrypoint &ep)
:
Session_rpc_object(tx_ds, ep),
_partition(partition), _tx_ds(tx_ds), _tx_thread(this)
{
_tx_thread.start();
}
void info(Genode::size_t *blk_count, Genode::size_t *blk_size, Operations *ops)
{
*blk_count = _partition->_sectors;
*blk_size = Partition::blk_size();
ops->set_operation(Packet_descriptor::READ);
ops->set_operation(Packet_descriptor::WRITE);
}
void sync() { Partition::sync(); }
Partition::Partition *partition() { return _partition; }
};
typedef Genode::Root_component<Session_component> Root_component;
/**
* Root component, handling new session requests
*/
class Root : public Root_component
{
private:
Genode::Rpc_entrypoint &_ep;
protected:
/**
* Always returns
*/
Session_component *_create_session(const char *args)
{
using namespace Genode;
Genode::size_t ram_quota =
Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
Genode::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 */
Genode::size_t session_size = max((Genode::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::partition(num)) {
PERR("Partition %ld unavailable", num);
throw Root::Unavailable();
}
return new (md_alloc())
Session_component(env()->ram_session()->alloc(tx_buf_size),
Partition::partition(num), _ep);
}
public:
Root(Genode::Rpc_entrypoint *session_ep, Genode::Allocator *md_alloc)
: Root_component(session_ep, md_alloc), _ep(*session_ep) { }
};
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;
try {
Partition::init();
} catch (Partition::Io_error) {
return -1;
if (!Block::Partition_table::table().avail()) {
PERR("No valid partition table found");
return 1;
}
enum { STACK_SIZE = 16384 };
enum { STACK_SIZE = 512 * 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());
static Block::Root block_root(&ep, env()->heap(), receiver);
env()->parent()->announce(ep.manage(&block_root));
sleep_forever();
while (true) {
Signal s = receiver.wait_for_signal();
static_cast<Signal_dispatcher_base *>(s.context())->dispatch(s.num());
}
return 0;
}

View File

@ -1,82 +0,0 @@
/*
* \brief Back-end header
* \author Sebastian Sumpf
* \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.
*/
#ifndef _PART_BLK_H_
#define _PART_BLK_H_
#include <base/exception.h>
#include <base/stdint.h>
namespace Partition {
enum {
MAX_PARTITIONS = 32, /* maximum supported paritions */
MAX_PACKET_SIZE = 1024 * 1024
};
/**
* The partition type
*/
struct Partition
{
Genode::uint32_t _lba; /* logical block address on device */
Genode::uint32_t _sectors; /* number of sectors in patitions */
Partition(Genode::uint32_t lba, Genode::uint32_t sectors)
: _lba(lba), _sectors(sectors) { }
/**
* Write/read blocks
*
* \param block_nr block number of partition to access
* \param count number of blocks to read/write
* \param buf buffer which containing the data to write or which is
* filled by reads
* \param write true for a write operations, false for a read
* \throw Io_error
*/
void io(unsigned long block_nr, unsigned long count, void *buf, bool write = false);
};
/**
* Excpetions
*/
class Io_error : public Genode::Exception {};
/**
* Initialize the back-end and parse partitions information
*
* \throw Io_error
*/
void init();
/**
* Return partition information
*
* \param num partition number
* \return pointer to partition if it could be found, zero otherwise
*/
Partition *partition(int num);
/**
* Returns block size of back end
*/
Genode::size_t blk_size();
/**
* Synchronize with backend device
*/
void sync();
}
#endif /* _PART_BLK_H_ */

View File

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

View File

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

View File

@ -1,115 +0,0 @@
/*
* \brief Test that reads and writes the first and the last block of a given
* block device
* \author Sebastian Sumpf
* \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 <base/allocator_avl.h>
#include <block_session/connection.h>
#include <os/config.h>
static const int verbose = 0;
using namespace Genode;
static Allocator_avl _block_alloc(env()->heap());
static Block::Connection _blk(&_block_alloc);
static size_t _blk_size;
class Sector
{
private:
Block::Packet_descriptor _p;
public:
Sector(unsigned long blk_nr, unsigned long count, bool write = false)
{
Block::Packet_descriptor::Opcode op = write ? Block::Packet_descriptor::WRITE
: Block::Packet_descriptor::READ;
try {
_p = Block::Packet_descriptor( _blk.dma_alloc_packet(_blk_size * count),
op, blk_nr, count);
} catch (Block::Session::Tx::Source::Packet_alloc_failed) {
PERR("Packet overrun!");
_p = _blk.tx()->get_acked_packet();
}
}
~Sector() { _blk.tx()->release_packet(_p); }
template <typename T>
T addr() { return reinterpret_cast<T>(_blk.tx()->packet_content(_p)); }
void submit_request()
{
_blk.tx()->submit_packet(_p);
_p = _blk.tx()->get_acked_packet();
if (!_p.succeeded())
PERR("Could not access block %llu", _p.block_number());
}
};
int main()
{
unsigned pattern;
try {
Genode::config()->xml_node().attribute("pattern").value(&pattern); }
catch (...) { PERR("Test Failed"); return 1; }
size_t blk_count;
Block::Session::Operations ops;
_blk.info(&blk_count, &_blk_size, &ops);
if (verbose)
printf("Found device %zu blocks of %zu bytes\n", blk_count, _blk_size);
/* write first and last block of device useing 'pattern' */
{
Sector s(0, 1, true);
memset(s.addr<void *>(), pattern, _blk_size);
s.submit_request();
Sector s_last(blk_count - 1, 1, true);
memset(s_last.addr<void *>(), 2 * pattern, _blk_size);
s_last.submit_request();
}
/* read first and last block from device and compare to 'pattern' */
Sector s(0, 1);
s.submit_request();
Sector s_last(blk_count - 1, 1);
s_last.submit_request();
unsigned *val = s.addr<unsigned *>();
unsigned *val_last = s_last.addr<unsigned *>();
unsigned cmp_val, cmp_val_last;
memset(&cmp_val, pattern, 4);
memset(&cmp_val_last, 2 * pattern, 4);
if (verbose) {
printf("READ blk %05u: %x\n", 0, *val);
printf("READ blk %05zu: %x\n", blk_count - 1, *val_last);
}
if (*val != cmp_val || *val_last != cmp_val_last)
printf("Failed\n");
else
printf("Success\n");
return 0;
}

View File

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

View File

@ -35,3 +35,4 @@ seoul-auto
resource_request
resource_yield
gdb_monitor
part_blk