diff --git a/libports/lib/mk/libc_block.mk b/libports/lib/mk/libc_block.mk
new file mode 100644
index 0000000000..f702219ee3
--- /dev/null
+++ b/libports/lib/mk/libc_block.mk
@@ -0,0 +1,9 @@
+LIBS = libc
+
+SRC_CC = plugin.cc
+
+vpath %.cc $(REP_DIR)/src/lib/libc_block
+
+include $(REP_DIR)/lib/mk/libc-common.inc
+
+SHARED_LIB = yes
diff --git a/libports/run/libc_block.run b/libports/run/libc_block.run
new file mode 100644
index 0000000000..928fa84b72
--- /dev/null
+++ b/libports/run/libc_block.run
@@ -0,0 +1,164 @@
+set use_sd_card_drv [expr [have_spec omap4] || [have_spec exynos5] || [have_spec pl180]]
+set use_atapi_drv [have_spec x86]
+
+if {[have_spec linux]} {
+ puts "Run script does not support this platform"; exit }
+
+if {[expr [have_spec 64bit] && $use_atapi_drv]} {
+ puts "ATAPI driver does not support 64 bit."; exit 0 }
+
+if {![is_qemu_available]} {
+ puts "\nPlease setup your native sd or hard drive. Remove this fail stop";
+ puts "check when you have prepared your native environment.\n";
+ exit 0
+}
+
+if {[have_spec x86]} {
+ set qemu "qemu-system-i386"
+ if {[have_spec 64bit]} { set qemu "qemu-system-x86_64" }
+
+ set version [exec $qemu --version]
+ set version [regexp -inline {[0-9]+\.[0-9]+\.[0-9]+} $version]
+
+ if {$version >= 1.5} {
+ puts "Atapi driver on Qemu $version known to be not working.";
+ puts "For more details see Genode issue 827 on github";
+
+ puts stderr "Test requires 'fix for issue 827'"
+ exit 0;
+ }
+}
+
+#
+# Build
+#
+
+set build_components {
+ core init
+ drivers/timer
+ test/libc_block
+}
+
+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
+#
+
+set 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
+#
+
+# generic modules
+set boot_modules {
+ core init timer
+ ld.lib.so libc.lib.so libc_log.lib.so libc_block.lib.so
+ test-libc_block
+}
+
+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
+
+#
+# Execute test case
+#
+
+set disk_image "bin/test.hda"
+set cmd "dd if=/dev/zero of=$disk_image bs=1024 count=65536"
+puts "creating disk image: $cmd"
+catch { exec sh -c $cmd }
+
+#
+# Qemu
+#
+append qemu_args " -m 128 -nographic "
+append_if $use_atapi_drv qemu_args " -hda $disk_image -boot order=d "
+append_if $use_sd_card_drv qemu_args " -drive file=$disk_image,if=sd,cache=writeback "
+
+run_genode_until {.*child exited with exit value 0.*} 60
+
+exec rm -f $disk_image
+
+puts "\nTest succeeded\n"
+
+# vi: set ft=tcl :
diff --git a/libports/src/lib/libc_block/plugin.cc b/libports/src/lib/libc_block/plugin.cc
new file mode 100644
index 0000000000..0dcc2cb01d
--- /dev/null
+++ b/libports/src/lib/libc_block/plugin.cc
@@ -0,0 +1,544 @@
+/*
+ * \brief Libc Block_session plugin
+ * \author Josef Soentgen
+ * \date 2013-11-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.
+ */
+
+/* Genode includes */
+#include
+#include
+#include
+
+/* libc plugin includes */
+#include
+#include
+
+/* libc includes */
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace Genode;
+
+
+static bool const verbose = false;
+
+/**
+ * There is a off_t typedef clash between sys/socket.h
+ * and base/stdint.h. We define the macro here to circumvent
+ * this issue.
+ */
+#undef DIOCGMEDIASIZE
+#define DIOCGMEDIASIZE _IOR('d', 129, Genode::off_t)
+
+/************
+ ** Plugin **
+ ************/
+
+namespace {
+
+ struct Plugin_context : Libc::Plugin_context
+ {
+ private:
+
+ Genode::Allocator *_md_alloc;
+ Genode::Allocator_avl _tx_block_alloc;
+
+ char *_block_buffer;
+
+ Block::Connection *_block;
+ size_t _block_size;
+ size_t _block_count;
+ Block::Session::Operations _block_ops;
+ Block::Session::Tx::Source *_tx_source;
+
+ ::off_t _cur_offset;
+
+ bool _readable;
+ bool _writeable;
+
+ int _flags;
+
+ public:
+
+
+ enum { BUFFER_BLOCK_NUMBER = 16 };
+
+ Plugin_context(Genode::Allocator *md_alloc, int flags)
+ :
+ _md_alloc(md_alloc),
+ _tx_block_alloc(_md_alloc),
+ _block_buffer(0),
+ _block(new (_md_alloc) Block::Connection(&_tx_block_alloc)),
+ _tx_source(_block->tx()),
+ _cur_offset(0),
+ _readable(false),
+ _writeable(false),
+ _flags(flags)
+ {
+ _block->info(&_block_count, &_block_size, &_block_ops);
+
+ _readable = _block_ops.supported(Block::Packet_descriptor::READ);
+ _writeable = _block_ops.supported(Block::Packet_descriptor::WRITE);
+
+ _block_buffer = reinterpret_cast(malloc(BUFFER_BLOCK_NUMBER * _block_size));
+
+ if (verbose) {
+ PDBG("number of blocks: %zu with block size: %zu (bytes)"
+ " , readable: %d writeable: %d",
+ _block_count, _block_size, _readable, _writeable);
+ }
+ }
+
+ ~Plugin_context()
+ {
+ destroy(_md_alloc, _block);
+ free(_block_buffer);
+ }
+
+ /**
+ * Return read/write state
+ */
+ bool readable() const { return _readable; }
+ bool writeable() const { return _writeable; }
+
+ /**
+ * Return file descriptor flags
+ */
+ int flags() const { return _flags; }
+
+ /**
+ * Return seek offset
+ */
+ ::off_t seek_offset() const { return _cur_offset; }
+
+ /**
+ * Set seek offset
+ */
+ void seek_offset(size_t seek_offset) { _cur_offset = seek_offset; }
+
+ /**
+ * Advance current seek offset position by 'incr' number of bytes
+ */
+ void advance_seek_offset(size_t incr) { _cur_offset += incr; }
+
+ /**
+ * Set seek offset to maximum value
+ */
+ void infinite_seek_offset() { _cur_offset = ~0; }
+
+ /**
+ * Return pointer to memory used as block puffer
+ */
+ char *block_buffer() const { return _block_buffer; }
+
+ /**
+ * Return number of blocks
+ */
+ size_t block_count() const { return _block_count; }
+
+ /**
+ * Return size of a block
+ */
+ size_t block_size() const { return _block_size; }
+
+ /**
+ * Issue a block operation
+ *
+ * This implementation operates synchronized, it will wait for the
+ * acknowlegdement for the submitted packet.
+ *
+ * \param nr number of start block
+ * \param buf buffer to read from or write to
+ * \param sz size of buffer
+ * \param write if true issue write operation, otherwise issue read opertion
+ * \param bulk if true operate on as many block as fit into the block buffer
+ *
+ * return number of bytes transfered
+ */
+ ssize_t block_io(size_t nr, void *buf, size_t sz, bool write, bool bulk = false)
+ {
+ /* sync offset */
+ _cur_offset = nr * _block_size;
+
+ Block::Packet_descriptor::Opcode op;
+ op = write ? Block::Packet_descriptor::WRITE : Block::Packet_descriptor::READ;
+
+ size_t packet_size = bulk ? sz : _block_size;
+ size_t packet_count = bulk ? (sz / _block_size) : 1;
+
+ /* sanity check */
+ if (packet_count > BUFFER_BLOCK_NUMBER) {
+ packet_size = BUFFER_BLOCK_NUMBER * _block_size;
+ packet_count = BUFFER_BLOCK_NUMBER;
+ }
+
+ if (verbose)
+ PDBG("%5s: block:%zu size:%zu packets:%zu",
+ write ? "write" : "read", nr, sz, packet_count);
+
+ Block::Packet_descriptor p(_tx_source->alloc_packet(packet_size), op,
+ nr, packet_count);
+
+ if (write)
+ Genode::memcpy(_tx_source->packet_content(p), buf, packet_size);
+
+ _tx_source->submit_packet(p);
+ p = _tx_source->get_acked_packet();
+
+ if (!p.succeeded()) {
+ PERR("Could not read block(s)");
+ _tx_source->release_packet(p);
+ errno = EIO;
+ return -1;
+ }
+
+ if (!write)
+ Genode::memcpy(buf, _tx_source->packet_content(p), packet_size);
+
+ _cur_offset += packet_size;
+
+ _tx_source->release_packet(p);
+ return packet_size;
+ }
+
+ };
+
+ static inline Plugin_context *context(Libc::File_descriptor *fd)
+ {
+ return static_cast(fd->context);
+ }
+
+ class Plugin : public Libc::Plugin
+ {
+ private:
+
+ /**
+ * File name this plugin feels responsible for
+ */
+ static char const *_dev_name() { return "/dev/blkdev"; }
+
+ bool _supports_path(const char *path)
+ {
+ return (Genode::strcmp(path, "/dev") == 0) ||
+ (Genode::strcmp(path, _dev_name()) == 0);
+ }
+
+ public:
+
+ /**
+ * Constructor
+ */
+ Plugin() { }
+
+ bool supports_open(const char *pathname, int flags) {
+ return _supports_path(pathname); }
+
+ bool supports_stat(const char *path) {
+ return _supports_path(path); }
+
+ int close(Libc::File_descriptor *fd)
+ {
+ Plugin_context *ctx = context(fd);
+
+ destroy(env()->heap(), ctx);
+ Libc::file_descriptor_allocator()->free(fd);
+
+ return 0;
+ }
+
+ int fcntl(Libc::File_descriptor *fd, int cmd, long arg)
+ {
+ switch (cmd) {
+ case F_GETFL:
+ return context(fd)->flags();
+ case F_SETLK:
+ /**
+ * We do not support locking a block device but we keep
+ * the caller happy.
+ */
+ return 0;
+ default:
+ PDBG("cmd: %d not implemented, return error.", cmd);
+ return -1;
+ }
+
+ return -1; /* never reached */
+ }
+
+ int fstat(Libc::File_descriptor *fd, struct stat *buf)
+ {
+ /* we pretent to be a block device */
+ if (buf) {
+ Genode::memset(buf, 0, sizeof (struct stat));
+ buf->st_mode = S_IFBLK;
+ }
+
+ return 0;
+ }
+
+ int fsync(Libc::File_descriptor *fd)
+ {
+ /**
+ * Currently all block I/O is done synchronized. To
+ * keep the caller happy we pretent that the fsync()
+ * call was successful.
+ */
+ return 0;
+ }
+
+ int ioctl(Libc::File_descriptor *fd, int req, char *argp)
+ {
+ Plugin_context *ctx = context(fd);
+
+ switch (req) {
+ case DIOCGMEDIASIZE:
+ return ctx->block_count();
+ case DIOCGSECTORSIZE:
+ return ctx->block_size();
+ default:
+ PDBG("request: %d not supported", req);
+ return -1;
+ }
+ }
+
+ ::off_t lseek(Libc::File_descriptor *fd, ::off_t offset, int whence)
+ {
+ Plugin_context *ctx = context(fd);
+
+ switch (whence) {
+ case SEEK_SET:
+ ctx->seek_offset(offset);
+ return offset;
+
+ case SEEK_CUR:
+ ctx->advance_seek_offset(offset);
+ return ctx->seek_offset();
+
+ case SEEK_END:
+ if (offset != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ ctx->infinite_seek_offset();
+ return (ctx->block_count() * ctx->block_size());
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ Libc::File_descriptor *open(const char *pathname, int flags)
+ {
+ try {
+ if (verbose)
+ PDBG("open block device '%s'", pathname);
+
+ Plugin_context *context = new (Genode::env()->heap())
+ Plugin_context(env()->heap(), flags);
+
+ return Libc::file_descriptor_allocator()->alloc(this, context);
+ } catch (...) {
+ PERR("could not create plugin context");
+ }
+
+ errno = ENOENT;
+ return 0;
+ }
+
+ ssize_t read(Libc::File_descriptor *fd, void *buf, ::size_t count)
+ {
+ Plugin_context *ctx = context(fd);
+
+ if (!ctx->readable()) {
+ PERR("block device is not readable");
+ errno = EINVAL;
+ return -1;
+ }
+
+ size_t blk_size = ctx->block_size();
+ char *blk_buffer = ctx->block_buffer();
+ char *_buf = (char*) buf;
+
+ ssize_t read = 0;
+ while (count > 0) {
+ size_t displ = 0;
+ size_t length = 0;
+ ssize_t nbytes = 0;
+ size_t blk_nr = ctx->seek_offset() / blk_size;
+
+ displ = ctx->seek_offset() % blk_size;
+
+ if ((displ + count) > blk_size)
+ length = (blk_size - displ);
+ else
+ length = count;
+
+ /**
+ * We take a shortcut and read the blocks all at once if the
+ * offset is aligned on a block boundary and we the count is a
+ * multiple of the block size, e.g. 4K reads will be read at
+ * once.
+ *
+ * XXX this is quite hackish because we have to omit partial
+ * blocks at the end.
+ */
+ if (displ == 0 && (count % blk_size) >= 0 && !(count < blk_size)) {
+ size_t bytes_left = count - (count % blk_size);
+
+ nbytes = ctx->block_io(blk_nr, _buf + read, bytes_left, false, true);
+ if (nbytes == -1) {
+ PERR("error while reading block:%zu from block device",
+ blk_nr);
+ return -1;
+ }
+
+ read += nbytes;
+ count -= nbytes;
+
+ continue;
+ }
+
+ if (displ > 0)
+ PWRN("offset:%lld is not aligned to block_size:%zu"
+ " displacement:%zu", ctx->seek_offset(), blk_size, displ);
+
+ nbytes = ctx->block_io(blk_nr, blk_buffer, blk_size, false);
+ if (nbytes != blk_size) {
+ PERR("error while reading block:%zu from block device",
+ blk_nr);
+ return -1;
+ }
+
+ Genode::memcpy(_buf + read, blk_buffer + displ, length);
+
+ read += length;
+ count -= length;
+ }
+
+ return read;
+ }
+
+ int stat(const char *path, struct stat *buf)
+ {
+ /* we pretent to be a block device */
+ if (buf) {
+ Genode::memset(buf, 0, sizeof (struct stat));
+ if (Genode::strcmp(path, "/dev") == 0)
+ buf->st_mode = S_IFDIR;
+ else if (Genode::strcmp(path, _dev_name()) == 0)
+ buf->st_mode = S_IFBLK;
+ else {
+ errno = ENOENT;
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ ssize_t write(Libc::File_descriptor *fd, const void *buf, ::size_t count)
+ {
+ Plugin_context *ctx = context(fd);
+
+ if (!ctx->writeable()) {
+ PERR("block device is not writeable");
+ errno = EINVAL;
+ return -1;
+ }
+
+ size_t blk_size = ctx->block_size();
+ char *blk_buffer = ctx->block_buffer();
+ const char *_buf = (const char*)buf;
+
+ ssize_t written = 0;
+ while (count > 0) {
+ size_t displ = 0;
+ size_t length = 0;
+ size_t nbytes = 0;
+ size_t blk_nr = ctx->seek_offset() / blk_size;
+
+ displ = ctx->seek_offset() % blk_size;
+
+ if ((displ + count) > blk_size)
+ length = (blk_size - displ);
+ else
+ length = count;
+
+ /**
+ * We take a shortcut and write as much as possible without
+ * using the block buffer if the offset is aligned on a block
+ * boundary and the count is a multiple of the block size,
+ * e.g. 4K writes will be written at once.
+ *
+ * XXX this is quite hackish because we have to omit partial
+ * blocks at the end.
+ */
+ if (displ == 0 && (count % blk_size) >= 0 && !(count < blk_size)) {
+ size_t bytes_left = count - (count % blk_size);
+
+ nbytes = ctx->block_io(blk_nr, (void*)(_buf + written),
+ bytes_left, true, true);
+ if (nbytes == -1) {
+ PERR("error while reading block:%zu from block device",
+ blk_nr);
+ return -1;
+ }
+
+ written += nbytes;
+ count -= nbytes;
+
+ continue;
+ }
+
+ /**
+ * The offset is not aligned on a block boundary. Therefore
+ * we need to read the block to the block buffer first and
+ * put the buffer content at the right offset before we can
+ * write the whole block back. In addition if length is less
+ * than block size, we also have to read the block first.
+ */
+ if (displ > 0 || length < blk_size) {
+ PWRN("offset:%lld block_size:%zd displacement:%zd length:%zu",
+ ctx->seek_offset(), blk_size, displ, length);
+
+ ctx->block_io(blk_nr, blk_buffer, blk_size, false);
+ /* rewind seek offset to account for the block read */
+ ctx->seek_offset(ctx->seek_offset()-blk_size);
+ }
+
+ Genode::memcpy(blk_buffer + displ, _buf + written, length);
+
+ nbytes = ctx->block_io(blk_nr, blk_buffer, blk_size, true);
+ if (nbytes != blk_size) {
+ PERR("error while reading block:%zu from Block_device",
+ blk_nr);
+ return -1;
+ }
+
+ written += length;
+ count -= length;
+ }
+
+ return written;
+ }
+
+ };
+
+} /* unnamed namespace */
+
+void __attribute__((constructor)) init_libc_block(void)
+{
+ PDBG("using the libc_block plugin");
+ static Plugin plugin;
+}
diff --git a/libports/src/test/libc_block/main.cc b/libports/src/test/libc_block/main.cc
new file mode 100644
index 0000000000..b64cd2545b
--- /dev/null
+++ b/libports/src/test/libc_block/main.cc
@@ -0,0 +1,78 @@
+/*
+ * \brief libc_block test
+ * \author Josef Soentgen
+ * \date 2013-11-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.
+ */
+
+/* Genode includes */
+#include
+
+/* libc includes */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+static char buf[16384];
+static char str[] = "deadbeef";
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ ssize_t n;
+ off_t offset;
+
+ printf("--- start test ---\n");
+
+ fd = open("/dev/blkdev", O_RDWR);
+ if (fd == -1) {
+ perror("open");
+ return 1;
+ }
+
+ offset = lseek(fd, 8193, SEEK_SET);
+ printf("offset: %zd\n", offset);
+
+ n = write(fd, str, sizeof (str));
+
+ offset = lseek(fd, 8193, SEEK_SET);
+ printf("offset: %zd\n", offset);
+ n = read(fd, buf, sizeof (str));
+ printf("bytes: %zd\n", n);
+ for (size_t i = 0; i < sizeof (str); i++)
+ printf("%c ", buf[i]);
+ printf("\n");
+
+ offset = lseek(fd, 16384, SEEK_SET);
+ n = write(fd, buf, sizeof (buf));
+ if (n != sizeof (buf))
+ printf("error write mismatch: %zd != %zu\n", n, sizeof (buf));
+
+ offset = lseek(fd, 4060, SEEK_SET);
+ n = write(fd, buf, sizeof (buf) / 2);
+ if (n != sizeof (buf)/2)
+ printf("error write mismatch: %zd != %zu\n", n, sizeof (buf)/2);
+
+ offset = lseek(fd, 2342, SEEK_SET);
+ n = read(fd, buf, sizeof (buf));
+ if (n != sizeof (buf))
+ printf("error read mismatch: %zd != %zu\n", n, sizeof (buf));
+
+ close(fd);
+
+ printf("--- test finished ---\n");
+
+ return 0;
+}
diff --git a/libports/src/test/libc_block/target.mk b/libports/src/test/libc_block/target.mk
new file mode 100644
index 0000000000..6e2161f4d4
--- /dev/null
+++ b/libports/src/test/libc_block/target.mk
@@ -0,0 +1,3 @@
+TARGET = test-libc_block
+LIBS = libc libc_log libc_block
+SRC_CC = main.cc