From af86e33c3f9104058403d6d06d9b83fc3f5d8f92 Mon Sep 17 00:00:00 2001 From: Stefan Kalkowski Date: Thu, 5 Dec 2013 15:02:44 +0100 Subject: [PATCH] part_blk: re-design to work event driven Fix #750 --- os/run/part_blk.run | 130 +++------- os/src/server/part_blk/back_end.cc | 244 ------------------- os/src/server/part_blk/component.h | 292 +++++++++++++++++++++++ os/src/server/part_blk/driver.h | 155 ++++++++++++ os/src/server/part_blk/main.cc | 227 ++---------------- os/src/server/part_blk/part_blk.h | 82 ------- os/src/server/part_blk/partition_table.h | 206 ++++++++++++++++ os/src/server/part_blk/target.mk | 2 +- os/src/test/part_blk/main.cc | 115 --------- os/src/test/part_blk/target.mk | 3 - tool/autopilot.list | 1 + 11 files changed, 708 insertions(+), 749 deletions(-) delete mode 100644 os/src/server/part_blk/back_end.cc create mode 100644 os/src/server/part_blk/component.h create mode 100644 os/src/server/part_blk/driver.h delete mode 100644 os/src/server/part_blk/part_blk.h create mode 100644 os/src/server/part_blk/partition_table.h delete mode 100644 os/src/test/part_blk/main.cc delete mode 100644 os/src/test/part_blk/target.mk diff --git a/os/run/part_blk.run b/os/run/part_blk.run index 3dd54e0a39..e7a8275087 100644 --- a/os/run/part_blk.run +++ b/os/run/part_blk.run @@ -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 { @@ -65,11 +57,16 @@ append config { + + + + + - + @@ -77,102 +74,33 @@ append config { - - + + - - - + + - -} - -append_if [have_spec acpi] config { - - - - - - - - - - - - } - -append_if [expr ![have_spec acpi] && [have_spec pci]] config { - - - - } - -append_if $use_atapi_drv config { - - - - - - -} - -append_if $use_sd_card_drv config { - - - - - -} - -append 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" diff --git a/os/src/server/part_blk/back_end.cc b/os/src/server/part_blk/back_end.cc deleted file mode 100644 index a9f7bbda7b..0000000000 --- a/os/src/server/part_blk/back_end.cc +++ /dev/null @@ -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 -#include -#include -#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 - T addr() { return reinterpret_cast(_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(); - - 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()); - } - - - void _io(unsigned long lba, unsigned long count, uint8_t *buf, bool write) - { - unsigned long bytes; - - while (count) { - - unsigned long curr_count = min(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(), bytes); - } - else { - memcpy(sec.addr(), 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); - } -} diff --git a/os/src/server/part_blk/component.h b/os/src/server/part_blk/component.h new file mode 100644 index 0000000000..44bd007eb8 --- /dev/null +++ b/os/src/server/part_blk/component.h @@ -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 +#include +#include + +#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::Element, + public Block_dispatcher +{ + private: + + Ram_dataspace_capability _rq_ds; + addr_t _rq_phys; + Partition *_partition; + Signal_dispatcher _sink_ack; + Signal_dispatcher _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& wait_queue() + { + static List 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 +{ + 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), + ""); + 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_ */ diff --git a/os/src/server/part_blk/driver.h b/os/src/server/part_blk/driver.h new file mode 100644 index 0000000000..05e342ac51 --- /dev/null +++ b/os/src/server/part_blk/driver.h @@ -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 +#include +#include +#include +#include +#include + +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::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 _r_slab; + Genode::List _r_list; + Genode::Allocator_avl _block_alloc; + Block::Connection _session; + Block::sector_t _blk_cnt; + Genode::size_t _blk_size; + Genode::Signal_dispatcher _source_ack; + Genode::Signal_dispatcher _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_ */ diff --git a/os/src/server/part_blk/main.cc b/os/src/server/part_blk/main.cc index 8d14dddedb..b6a589b9a3 100644 --- a/os/src/server/part_blk/main.cc +++ b/os/src/server/part_blk/main.cc @@ -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 #include #include #include -#include -#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 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), ""); - - 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(s.context())->dispatch(s.num()); + } + return 0; } diff --git a/os/src/server/part_blk/part_blk.h b/os/src/server/part_blk/part_blk.h deleted file mode 100644 index a2ee96aff1..0000000000 --- a/os/src/server/part_blk/part_blk.h +++ /dev/null @@ -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 -#include - -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_ */ diff --git a/os/src/server/part_blk/partition_table.h b/os/src/server/part_blk/partition_table.h new file mode 100644 index 0000000000..539adbe486 --- /dev/null +++ b/os/src/server/part_blk/partition_table.h @@ -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 +#include +#include + +#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 T addr() { + return reinterpret_cast(_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(); + + 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()); + + /* + * 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_ */ diff --git a/os/src/server/part_blk/target.mk b/os/src/server/part_blk/target.mk index 7653984545..c2965d314b 100644 --- a/os/src/server/part_blk/target.mk +++ b/os/src/server/part_blk/target.mk @@ -1,3 +1,3 @@ TARGET = part_blk LIBS = base config -SRC_CC = main.cc back_end.cc +SRC_CC = main.cc diff --git a/os/src/test/part_blk/main.cc b/os/src/test/part_blk/main.cc deleted file mode 100644 index d7484e1e20..0000000000 --- a/os/src/test/part_blk/main.cc +++ /dev/null @@ -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 -#include -#include - -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 - T addr() { return reinterpret_cast(_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(), pattern, _blk_size); - s.submit_request(); - - Sector s_last(blk_count - 1, 1, true); - memset(s_last.addr(), 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 *val_last = s_last.addr(); - - 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; -} diff --git a/os/src/test/part_blk/target.mk b/os/src/test/part_blk/target.mk deleted file mode 100644 index 521e963e08..0000000000 --- a/os/src/test/part_blk/target.mk +++ /dev/null @@ -1,3 +0,0 @@ -TARGET = test-part -SRC_CC = main.cc -LIBS = base config diff --git a/tool/autopilot.list b/tool/autopilot.list index 628050be66..e21b850fb1 100644 --- a/tool/autopilot.list +++ b/tool/autopilot.list @@ -35,3 +35,4 @@ seoul-auto resource_request resource_yield gdb_monitor +part_blk