diff --git a/repos/os/run/lx_block.run b/repos/os/run/lx_block.run new file mode 100644 index 000000000..0138bb9d0 --- /dev/null +++ b/repos/os/run/lx_block.run @@ -0,0 +1,59 @@ +# +# Test runs only on Linux +# +assert_spec linux + +# +# Build +# +build { core init drivers/timer server/lx_block test/blk/bench } + +create_boot_directory + +# +# Generate config +# +install_config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +# +# Create test file +# +catch { exec dd if=/dev/zero of=bin/lx_block.img bs=1M count=64 } + +# +# Boot modules +# +build_boot_image { core ld.lib.so init timer lx_block lx_block.img test-blk-bench } + +run_genode_until "Done.*\n" 100 + +exec rm -f bin/lx_block.img diff --git a/repos/os/src/server/lx_block/README b/repos/os/src/server/lx_block/README new file mode 100644 index 000000000..94f1cd9e6 --- /dev/null +++ b/repos/os/src/server/lx_block/README @@ -0,0 +1,22 @@ +This directory contains an Genode Block service that uses a file on +a Linux system as backing store. + +Configuration +~~~~~~~~~~~~~ + +The backing store is specified by the 'file' attribute. The pretended +block size is specified by the 'block_size' attribute. In case it is +omitted, a default block size of 512 bytes is used. To allow write +access, the 'writeable' attribute must be set to 'yes'. By default only +read-only access it allowed. + +An example configuration is shown in the the following config snippet: + +! + + +Notes +~~~~~ + +The backing file is opened with blocking semantics and thereby the block +session is used synchronously. diff --git a/repos/os/src/server/lx_block/main.cc b/repos/os/src/server/lx_block/main.cc new file mode 100644 index 000000000..294776833 --- /dev/null +++ b/repos/os/src/server/lx_block/main.cc @@ -0,0 +1,194 @@ +/* + * \brief Linux file as a block session + * \author Josef Soentgen + * \date 2017-07-05 + */ + +/* + * Copyright (C) 2017 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 +#include +#include +#include +#include + +/* libc includes */ +#include +#include +#include +#include +#include /* perror */ + + +static bool xml_attr_ok(Genode::Xml_node node, char const *attr) +{ + return node.attribute_value(attr, false); +} + + +class Lx_block_driver : public Block::Driver +{ + private: + + Genode::Env &_env; + + Block::sector_t _block_count { 0 }; + Genode::size_t _block_size { 512 }; + Block::Session::Operations _block_ops; + + int _fd { -1 }; + + public: + + struct Could_not_open_file : Genode::Exception { }; + + Lx_block_driver(Genode::Env &env, Genode::Xml_node config) + : Block::Driver(env.ram()), _env(env) + { + Genode::String<256> file; + try { + config.attribute("file").value(&file); + } catch (...) { + Genode::error("mandatory file attribute missing"); + throw Could_not_open_file(); + } + + try { + Genode::Number_of_bytes bytes; + config.attribute("block_size").value(&bytes); + _block_size = bytes; + } catch (...) { + Genode::warning("block size missing, assuming 512b"); + } + + bool const writeable = xml_attr_ok(config, "writeable"); + + struct stat st; + if (stat(file.string(), &st)) { + perror("stat"); + throw Could_not_open_file(); + } + + _block_count = st.st_size / _block_size; + + /* open file */ + _fd = open(file.string(), writeable ? O_RDWR : O_RDONLY); + if (_fd == -1) { + perror("open"); + throw Could_not_open_file(); + } + + _block_ops.set_operation(Block::Packet_descriptor::READ); + if (writeable) { + _block_ops.set_operation(Block::Packet_descriptor::WRITE); + } + + Genode::log("Provide '", file.string(), "' as block device " + "block_size: ", _block_size, " block_count: ", + _block_count, " writeable: ", writeable ? "yes" : "no"); + } + + ~Lx_block_driver() { close(_fd); } + + + /***************************** + ** Block::Driver interface ** + *****************************/ + + Genode::size_t block_size() override { return _block_size; } + Block::sector_t block_count() override { return _block_count; } + Block::Session::Operations ops() override { return _block_ops; } + + void read(Block::sector_t block_number, + Genode::size_t block_count, + char *buffer, + Block::Packet_descriptor &packet) override + { + /* range check is done by Block::Driver */ + if (!_block_ops.supported(Block::Packet_descriptor::READ)) { + throw Io_error(); + } + + off_t const offset = block_number * _block_size; + size_t const count = block_count * _block_size; + + ssize_t const n = pread(_fd, buffer, count, offset); + if (n == -1) { + perror("pread"); + throw Io_error(); + } + + ack_packet(packet); + } + + void write(Block::sector_t block_number, + Genode::size_t block_count, + char const *buffer, + Block::Packet_descriptor &packet) override + { + /* range check is done by Block::Driver */ + if (!_block_ops.supported(Block::Packet_descriptor::WRITE)) { + throw Io_error(); + } + + off_t const offset = block_number * _block_size; + size_t const count = block_count * _block_size; + + ssize_t const n = pwrite(_fd, buffer, count, offset); + if (n == -1) { + perror("pwrite"); + throw Io_error(); + } + + ack_packet(packet); + } + + void sync() { } +}; + + +struct Main +{ + Genode::Env &_env; + Genode::Heap _heap { _env.ram(), _env.rm() }; + + Genode::Attached_rom_dataspace _config_rom { _env, "config" }; + + struct Factory : Block::Driver_factory + { + Genode::Constructible _driver; + + Factory(Genode::Env &env, Genode::Xml_node config) + { + _driver.construct(env, config); + } + + ~Factory() { _driver.destruct(); } + + /*********************** + ** Factory interface ** + ***********************/ + + Block::Driver *create() { return &*_driver; } + void destroy(Block::Driver *) { } + } factory { _env, _config_rom.xml() }; + + Block::Root root { _env.ep(), _heap, _env.rm(), factory, + xml_attr_ok(_config_rom.xml(), "writeable") }; + + Main(Genode::Env &env) : _env(env) + { + _env.parent().announce(_env.ep().manage(root)); + } +}; + + +void Component::construct(Genode::Env &env) { static Main main(env); } diff --git a/repos/os/src/server/lx_block/target.mk b/repos/os/src/server/lx_block/target.mk new file mode 100644 index 000000000..23d4b49f9 --- /dev/null +++ b/repos/os/src/server/lx_block/target.mk @@ -0,0 +1,7 @@ +TARGET = lx_block +REQUIRES = linux + +SRC_CC = main.cc +LIBS = lx_hybrid + +INC_DIR += $(PRG_DIR) /usr/include