diff --git a/repos/os/src/server/part_block/README b/repos/os/src/server/part_block/README index fe15fca6e..a925726b1 100644 --- a/repos/os/src/server/part_block/README +++ b/repos/os/src/server/part_block/README @@ -70,8 +70,9 @@ Configuration snippet with two clients and an (hypothetical) IDE driver: ! ! ! -! +! 'test-part2' receives access to primary partition 1, the buffer between +! the 'ata_driver' and 'part_block' is 1 MeB ('io_buffer') --> +! ! ! ! diff --git a/repos/os/src/server/part_block/component.h b/repos/os/src/server/part_block/component.h deleted file mode 100644 index 391866dbc..000000000 --- a/repos/os/src/server/part_block/component.h +++ /dev/null @@ -1,361 +0,0 @@ -/* - * \brief Block-session component for partition server - * \author Stefan Kalkowski - * \date 2013-12-04 - */ - -/* - * Copyright (C) 2013-2017 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU Affero General Public License version 3. - */ - -#ifndef _PART_BLOCK__COMPONENT_H_ -#define _PART_BLOCK__COMPONENT_H_ - -#include -#include -#include -#include -#include - -#include "gpt.h" - -namespace Block { - - using namespace Genode; - - class Session_component; - class Root; -}; - - -class Block::Session_component : public Block::Session_rpc_object, - private List::Element, - public Block_dispatcher -{ - private: - - friend class List; - - /* - * Noncopyable - */ - Session_component(Session_component const &); - Session_component &operator = (Session_component const &); - - Ram_dataspace_capability _rq_ds; - addr_t _rq_phys; - Partition *_partition; - Signal_handler _sink_ack; - Signal_handler _sink_submit; - bool _req_queue_full; - bool _ack_queue_full; - Packet_descriptor _p_to_handle { }; - unsigned _p_in_fly; - Block::Driver &_driver; - bool _writeable; - - /** - * Acknowledge a packet already handled - */ - inline void _ack_packet(Packet_descriptor &packet) - { - if (!tx_sink()->ready_to_ack()) - error("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.size() || !_range_check(_p_to_handle)) { - _ack_packet(_p_to_handle); - return; - } - - sector_t const off = _p_to_handle.block_number() + _partition->lba; - size_t const cnt = _p_to_handle.block_count(); - - auto perform_io = [&] () - { - bool const write = - (_p_to_handle.operation() == Packet_descriptor::WRITE); - - try { - _driver.io(write, off, cnt, - tx_sink()->packet_content(_p_to_handle), - *this, _p_to_handle); - } catch (Block::Session::Tx::Source::Packet_alloc_failed) { - if (!_req_queue_full) { - _req_queue_full = true; - Session_component::wait_queue().insert(this); - } - } catch (Genode::Packet_descriptor::Invalid_packet) { - Genode::error("dropping invalid Block packet"); - _p_to_handle = Packet_descriptor(); - } - }; - - switch (_p_to_handle.operation()) { - - case Packet_descriptor::READ: - perform_io(); - break; - - case Packet_descriptor::WRITE: - - if (!_writeable) { - _ack_packet(_p_to_handle); - return; - } - - perform_io(); - break; - - case Packet_descriptor::SYNC: - _driver.sync_all(*this, _p_to_handle); - break; - - case Packet_descriptor::TRIM: - case Packet_descriptor::END: - - _p_to_handle.succeeded(true); - _ack_packet(_p_to_handle); - break; - } - } - - /** - * Triggered when a packet was placed into the empty submit queue - */ - void _packet_avail() - { - _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() { _packet_avail(); } - - public: - - /** - * Constructor - */ - Session_component(Ram_dataspace_capability rq_ds, - Partition *partition, - Genode::Entrypoint &ep, - Genode::Region_map &rm, - Block::Driver &driver, - bool writeable) - : Session_rpc_object(rm, rq_ds, ep.rpc_ep()), - _rq_ds(rq_ds), - _rq_phys(Dataspace_client(_rq_ds).phys_addr()), - _partition(partition), - _sink_ack(ep, *this, &Session_component::_ready_to_ack), - _sink_submit(ep, *this, &Session_component::_packet_avail), - _req_queue_full(false), - _ack_queue_full(false), - _p_in_fly(0), - _driver(driver), - _writeable(writeable) - { - _tx.sigh_ready_to_ack(_sink_ack); - _tx.sigh_packet_avail(_sink_submit); - } - - ~Session_component() - { - _driver.remove_dispatcher(*this); - - if (_req_queue_full) - wait_queue().remove(this); - } - - Ram_dataspace_capability const rq_ds() const { return _rq_ds; } - Partition *partition() { return _partition; } - - void dispatch(Packet_descriptor &request, Packet_descriptor &reply) override - { - request.succeeded(reply.succeeded()); - - if (request.operation() == Block::Packet_descriptor::READ) { - void *src = - _driver.session().tx()->packet_content(reply); - Genode::size_t sz = - request.block_count() * _driver.blk_size(); - try { Genode::memcpy(tx_sink()->packet_content(request), src, sz); } - catch (Genode::Packet_descriptor::Invalid_packet) { - request.succeeded(false); - } - } - - _ack_packet(request); - - if (_ack_queue_full) - _packet_avail(); - } - - static List& wait_queue() - { - static List l; - return l; - } - - static void wake_up() - { - for (; Session_component *c = wait_queue().first();) - { - wait_queue().remove(c); - c->_req_queue_full = false; - c->_handle_packet(c->_p_to_handle); - c->_packet_avail(); - } - } - - /******************************* - ** Block session interface ** - *******************************/ - - Info info() const override - { - return Info { .block_size = _driver.blk_size(), - .block_count = _partition->sectors, - .align_log2 = Genode::log2(_driver.blk_size()), - .writeable = _writeable && _driver.writeable() }; - } -}; - - -/** - * Root component, handling new session requests - */ -class Block::Root : - public Genode::Root_component -{ - private: - - Genode::Env &_env; - Genode::Xml_node _config; - Block::Driver &_driver; - Block::Partition_table &_table; - - protected: - - void _destroy_session(Session_component *session) override - { - Ram_dataspace_capability rq_ds = session->rq_ds(); - Genode::Root_component::_destroy_session(session); - _env.ram().free(rq_ds); - } - - /** - * Always returns the singleton block-session component - */ - Session_component *_create_session(const char *args) override - { - long num = -1; - bool writeable = false; - - Session_label const label = label_from_args(args); - char const *label_str = label.string(); - try { - Session_policy policy(label, _config); - - /* read partition attribute */ - num = policy.attribute_value("partition", -1L); - - /* sessions are not writeable by default */ - writeable = policy.attribute_value("writeable", false); - - } catch (Xml_node::Nonexistent_attribute) { - error("policy does not define partition number for for '", - label_str, "'"); - throw Service_denied(); - } catch (Session_policy::No_policy_defined) { - error("rejecting session request, no matching policy for '", - label_str, "'"); - throw Service_denied(); - } - - if (!_table.partition(num)) { - error("Partition ", num, " unavailable for '", label_str, "'"); - throw Service_denied(); - } - - 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); - - if (!tx_buf_size) - throw Service_denied(); - - /* 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 Insufficient_ram_quota(); - - /* - * 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) { - error("insufficient 'ram_quota', got ", ram_quota, ", need ", - tx_buf_size + session_size); - throw Insufficient_ram_quota(); - } - - if (writeable) - writeable = Arg_string::find_arg(args, "writeable").bool_value(true); - - Ram_dataspace_capability ds_cap; - ds_cap = _env.ram().alloc(tx_buf_size); - Session_component *session = new (md_alloc()) - Session_component(ds_cap, _table.partition(num), - _env.ep(), _env.rm(), _driver, - writeable); - - log("session opened at partition ", num, " for '", label_str, "'"); - return session; - } - - public: - - Root(Genode::Env &env, Genode::Xml_node config, Genode::Heap &heap, - Block::Driver &driver, Block::Partition_table &table) - : Root_component(env.ep(), heap), _env(env), _config(config), - _driver(driver), _table(table) { } -}; - -#endif /* _PART_BLOCK__COMPONENT_H_ */ diff --git a/repos/os/src/server/part_block/driver.h b/repos/os/src/server/part_block/driver.h deleted file mode 100644 index 21e2ace49..000000000 --- a/repos/os/src/server/part_block/driver.h +++ /dev/null @@ -1,194 +0,0 @@ -/* - * \brief Block-session driver for partition server - * \author Stefan Kalkowski - * \date 2013-12-04 - */ - -/* - * Copyright (C) 2013-2017 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU Affero General Public License version 3. - */ - -#ifndef _PART_BLOCK__DRIVER_H_ -#define _PART_BLOCK__DRIVER_H_ - -#include -#include -#include -#include -#include -#include -#include - -namespace Block { - class Block_dispatcher; - class Driver; -}; - - -struct Block::Block_dispatcher : Genode::Interface -{ - virtual void dispatch(Packet_descriptor&, Packet_descriptor&) = 0; -}; - - -bool operator== (const Block::Packet_descriptor& p1, - const Block::Packet_descriptor& p2) -{ - return p1.tag().value == p2.tag().value; -} - - -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 const &cli, - Packet_descriptor const &srv) - : _dispatcher(d), _cli(cli), _srv(srv) {} - - bool handle(Packet_descriptor& reply) - { - bool ret = (reply == _srv); - if (ret) _dispatcher.dispatch(_cli, reply); - return ret; - } - - bool same_dispatcher(Block_dispatcher &same) { - return &same == &_dispatcher; } - }; - - 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::Session::Info const _info { _session.info() }; - Genode::Signal_handler _source_ack; - Genode::Signal_handler _source_submit; - unsigned long _tag_cnt { 0 }; - - void _ready_to_submit(); - - void _ack_avail() - { - /* 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(); - } - - Block::Session::Tag _alloc_tag() - { - /* - * The wrapping of '_tag_cnt' is no problem because the number - * of consecutive outstanding requests is much lower than the - * value range of tags. - */ - _tag_cnt++; - return Block::Session::Tag { _tag_cnt }; - } - - public: - - Driver(Genode::Env &env, Genode::Heap &heap) - : _r_slab(&heap), - _block_alloc(&heap), - _session(env, &_block_alloc, 4 * 1024 * 1024), - _source_ack(env.ep(), *this, &Driver::_ack_avail), - _source_submit(env.ep(), *this, &Driver::_ready_to_submit) - { } - - Genode::size_t blk_size() const { return _info.block_size; } - Genode::size_t blk_cnt() const { return _info.block_count; } - bool writeable() const { return _info.writeable; } - - 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 const size = _info.block_size * cnt; - Packet_descriptor p(_session.alloc_packet(size), - op, nr, cnt, _alloc_tag()); - 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); - } - - void sync_all(Block_dispatcher &dispatcher, Packet_descriptor &cli) - { - if (!_session.tx()->ready_to_submit()) - throw Block::Session::Tx::Source::Packet_alloc_failed(); - - Packet_descriptor const p = - Block::Session::sync_all_packet_descriptor(_info, _alloc_tag()); - - _r_list.insert(new (&_r_slab) Request(dispatcher, cli, p)); - - _session.tx()->submit_packet(p); - } - - void remove_dispatcher(Block_dispatcher &dispatcher) - { - for (Request *r = _r_list.first(); r;) { - if (!r->same_dispatcher(dispatcher)) { - r = r->next(); - continue; - } - - Request *remove = r; - r = r->next(); - - _r_list.remove(remove); - Genode::destroy(&_r_slab, remove); - } - } -}; - -#endif /* _PART_BLOCK__DRIVER_H_ */ diff --git a/repos/os/src/server/part_block/gpt.h b/repos/os/src/server/part_block/gpt.h index c3940beb5..8eb9b4200 100644 --- a/repos/os/src/server/part_block/gpt.h +++ b/repos/os/src/server/part_block/gpt.h @@ -1,11 +1,12 @@ /* * \brief GUID Partition table definitions * \author Josef Soentgen + * \author Sebastian Sumpf * \date 2014-09-19 */ /* - * Copyright (C) 2014-2017 Genode Labs GmbH + * Copyright (C) 2014-2020 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. @@ -18,151 +19,170 @@ #include #include #include +#include +#include -#include "driver.h" #include "partition_table.h" #include "fsprobe.h" -namespace { +static bool constexpr verbose = false; -static bool const verbose = false; +namespace Block { + class Gpt; +}; -/* simple bitwise CRC32 checking */ -static inline Genode::uint32_t crc32(void const * const buf, Genode::size_t size) -{ - Genode::uint8_t const *p = static_cast(buf); - Genode::uint32_t crc = ~0U; - - while (size--) { - crc ^= *p++; - for (Genode::uint32_t j = 0; j < 8; j++) - crc = (-Genode::int32_t(crc & 1) & 0xedb88320) ^ (crc >> 1); - } - - return crc ^ ~0U; -} - -} /* anonymous namespace */ - - -class Gpt : public Block::Partition_table +class Block::Gpt : public Block::Partition_table { private: enum { MAX_PARTITIONS = 128 }; - /* contains pointers to valid partitions or 0 */ - Block::Partition *_part_list[MAX_PARTITIONS] { 0 }; + /* contains valid partitions or not constructed */ + Constructible _part_list[MAX_PARTITIONS]; typedef Block::Partition_table::Sector Sector; - /** * DCE uuid struct */ - struct Uuid + struct Uuid : Mmio { - enum { UUID_NODE_LEN = 6 }; + struct Time_low : Register<0, 32> { }; + struct Time_mid : Register<4, 16> { }; + struct Time_hi_and_version : Register<6, 16> { }; - Genode::uint32_t time_low; - Genode::uint16_t time_mid; - Genode::uint16_t time_hi_and_version; - Genode::uint8_t clock_seq_hi_and_reserved; - Genode::uint8_t clock_seq_low; - Genode::uint8_t node[UUID_NODE_LEN]; + struct Clock_seq_hi_and_reserved : Register<8, 8> { }; + struct Clock_seq_low : Register<9, 8> { }; - char const *to_string() + struct Node : Register_array<10, 8, 6, 8> { }; + + Uuid() = delete; + Uuid(addr_t base) : Mmio(base) { }; + + unsigned time_low() const { return read(); } + + template struct Uuid_hex : Genode::Hex { + Uuid_hex(T value) : Genode::Hex(value, OMIT_PREFIX, PAD) { } }; + + void print(Output &out) const { - static char buffer[37 + 1]; + Genode::print(out, Uuid_hex(read()), + "-", Uuid_hex(read()), + "-", Uuid_hex(read()), + "-", Uuid_hex(read()), + Uuid_hex(read()), + "-"); - Genode::snprintf(buffer, sizeof(buffer), - "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", - time_low, time_mid, time_hi_and_version, - clock_seq_hi_and_reserved, clock_seq_low, - node[0], node[1], node[2], node[3], node[4], node[5]); - - return buffer; + for (unsigned i = 0; i < 6; i++) + Genode::print(out, Uuid_hex(read(i))); } - - } __attribute__((packed)); + static constexpr size_t size() { return 16; } + }; /** * GUID parition table header */ - struct Gpt_hdr - { - enum { HEADER_LBA = 1 }; + struct Gpt_hdr : Mmio - char _sig[8]; /* identifies GUID Partition Table */ - Genode::uint32_t _revision; /* GPT specification revision */ - Genode::uint32_t _hdr_size; /* size of GPT header */ - Genode::uint32_t _hdr_crc; /* CRC32 of GPT header */ - Genode::uint32_t _reserved; /* must be zero */ - Genode::uint64_t _hdr_lba; /* LBA that contains this header */ - Genode::uint64_t _backup_hdr_lba; /* LBA of backup GPT header */ - Genode::uint64_t _part_lba_start; /* first LBA usable for partitions */ - Genode::uint64_t _part_lba_end; /* last LBA usable for partitions */ - Uuid _guid; /* GUID to identify the disk */ - Genode::uint64_t _gpe_lba; /* first LBA of GPE array */ - Genode::uint32_t _entries; /* number of entries in GPE array */ - Genode::uint32_t _entry_size; /* size of each GPE */ - Genode::uint32_t _gpe_crc; /* CRC32 of GPE array */ + { + struct Sig : Register<0, 64> { }; /* identifies GUID Partition Table */ + struct Revision : Register<8, 32> { }; /* GPT specification revision */ + struct Hdr_size : Register<12, 32> { }; /* size of GPT header */ + struct Hdr_crc : Register<16, 32> { }; /* CRC32 of GPT header */ + struct Reserved : Register<20,32> { }; /* must be zero */ + struct Hdr_lba : Register<24, 64> { + enum { LBA = 1 }; }; /* LBA that contains this header */ + + struct Backup_hdr_lba : Register<32, 64> { }; /* LBA of backup GPT header */ + struct Part_lba_start : Register<40, 64> { }; /* first LBA usable for partitions */ + struct Part_lba_end : Register<48, 64> { }; /* last LBA usable for partitions */ + + Uuid guid() { return Uuid(base() + 56); } /* GUID to identify the disk */ + + struct Gpe_lba : Register<72, 64> { }; /* first LBA of GPE array */ + struct Entries : Register<80, 32> { }; /* number of entries in GPE array */ + struct Entry_size : Register<84, 32> { }; /* size of each GPE */ + struct Gpe_crc : Register<88, 32> { }; /* CRC32 of GPE array */ + + Gpt_hdr() = delete; + Gpt_hdr(addr_t base) : Mmio(base) { }; + + uint64_t part_lba_start() const { return read(); } + uint64_t part_lba_end() const { return read(); } + uint64_t gpe_lba() const { return read(); } + uint32_t entries() const { return read(); } + uint32_t entry_size() const { return read(); } + + uint32_t crc32(addr_t buf, size_t size) + { + uint8_t const *p = reinterpret_cast(buf); + uint32_t crc = ~0U; + + while (size--) { + crc ^= *p++; + for (uint32_t j = 0; j < 8; j++) + crc = (-int32_t(crc & 1) & 0xedb88320) ^ (crc >> 1); + } + + return crc ^ ~0U; + } void dump_hdr(bool check_primary) { if (!verbose) return; - using namespace Genode; - log("GPT ", check_primary ? "primary" : "backup", " header:"); - log(" rev: ", (unsigned) _revision); - log(" size: ", (unsigned) _hdr_size); - log(" crc: ", Hex(_hdr_crc, Hex::OMIT_PREFIX)); - log(" reserved: ", (unsigned) _reserved); - log(" hdr lba: ", (unsigned long long) _hdr_lba); - log(" bak lba: ", (unsigned long long) _backup_hdr_lba); - log(" part start lba: ", (unsigned long long) _part_lba_start); - log(" part end lba: ", (unsigned long long) _part_lba_end); - log(" guid: ", _guid.to_string()); - log(" gpe lba: ", (unsigned long long) _gpe_lba); - log(" entries: ", (unsigned) _entries); - log(" entry size: ", (unsigned) _entry_size); - log(" gpe crc: ", Hex(_gpe_crc, Hex::OMIT_PREFIX)); + log(" rev: ", read()); + log(" size: ", read()); + log(" crc: ", Hex(read(), Hex::OMIT_PREFIX)); + log(" reserved: ", read()); + log(" hdr lba: ", read()); + log(" bak lba: ", read()); + log(" part start lba: ", read()); + log(" part end lba: ", read()); + log(" guid: ", guid()); + log(" gpe lba: ", read()); + log(" entries: ", read()); + log(" entry size: ", read()); + log(" gpe crc: ", Hex(read(), Hex::OMIT_PREFIX)); } - bool valid(Block::Driver &driver, bool check_primary = true) + bool valid(Partition_table::Sector_data &data, bool check_primary = true) { dump_hdr(check_primary); /* check sig */ - if (Genode::strcmp(_sig, "EFI PART", 8) != 0) + uint64_t const magic = 0x5452415020494645; /* "EFI PART" - ascii */; + if (read() != magic) { return false; + } /* check header crc */ - Genode::uint32_t crc = _hdr_crc; - _hdr_crc = 0; - if (crc32(this, _hdr_size) != crc) { - Genode::error("Wrong GPT header checksum"); + uint32_t crc = read(); + write(0); + if (crc32(base(), read()) != crc) { + error("Wrong GPT header checksum"); return false; } /* check header lba */ if (check_primary) - if (_hdr_lba != HEADER_LBA) + if (read() != Hdr_lba::LBA) return false; /* check GPT entry array */ - Genode::size_t length = _entries * _entry_size; - Sector gpe(driver, _gpe_lba, length / driver.blk_size()); - if (crc32(gpe.addr(), length) != _gpe_crc) + size_t length = entries() * entry_size(); + Sector gpe(data, gpe_lba(), length / data.block.info().block_size); + if (crc32(gpe.addr(), length) != read()) return false; if (check_primary) { /* check backup gpt header */ - Sector backup_hdr(driver, _backup_hdr_lba, 1); - if (!backup_hdr.addr()->valid(driver, false)) { - Genode::warning("Backup GPT header is corrupted"); + Sector backup_hdr(data, read(), 1); + Gpt_hdr backup(backup_hdr.addr()); + if (!backup.valid(data, false)) { + warning("Backup GPT header is corrupted"); } } @@ -170,60 +190,53 @@ class Gpt : public Block::Partition_table } /* the remainder of the LBA must be zero */ - } __attribute__((packed)); + }; /** * GUID partition entry format */ - struct Gpt_entry + struct Gpt_entry : Mmio { enum { NAME_LEN = 36 }; - Uuid _type; /* partition type GUID */ - Uuid _guid; /* unique partition GUID */ - Genode::uint64_t _lba_start; /* start of partition */ - Genode::uint64_t _lba_end; /* end of partition */ - Genode::uint64_t _attr; /* partition attributes */ - Genode::uint16_t _name[NAME_LEN]; /* partition name in UNICODE-16 */ + + Uuid type() const { return Uuid(base()); } /* partition type GUID */ + Uuid guid() const { return Uuid(base()+ Uuid::size()); } /* unique partition GUID */ + + struct Lba_start : Register<32, 64> { }; /* start of partition */ + struct Lba_end : Register<40, 64> { }; /* end of partition */ + struct Attr : Register<48, 64> { }; /* partition attributes */ + struct Name : Register_array<56, 16, NAME_LEN, 16> { }; /* partition name in UNICODE-16 */ + + Gpt_entry() = delete; + Gpt_entry(addr_t base) : Mmio(base) { } + + uint64_t lba_start() const { return read(); } + uint64_t lba_end() const { return read(); } bool valid() const { - if (_type.time_low == 0x00000000) + if (type().time_low() == 0x00000000) return false; return true; } /** - * Extract all valid ASCII characters in the name entry + * Extract UTF-8 for name entry */ - char const *name() + void print(Output &out) const { - static char buffer[NAME_LEN + 1]; + for (unsigned i = 0; i < NAME_LEN; i++) { + uint32_t utf16 = read(i); - char *p = buffer; - Genode::size_t i = 0; + if (utf16 == 0) break; - for (Genode::size_t u = 0; u < NAME_LEN && _name[u] != 0; u++) { - Genode::uint32_t utfchar = _name[i++]; - - if ((utfchar & 0xf800) == 0xd800) { - unsigned int c = _name[i]; - if ((utfchar & 0x400) != 0 || (c & 0xfc00) != 0xdc00) - utfchar = 0xfffd; - else - i++; - } - - *p++ = (utfchar < 0x80) ? utfchar : '.'; + Codepoint code { utf16 }; + code.print(out); } - - *p++ = 0; - - return buffer; } - - } __attribute__((packed)); + }; /** @@ -236,39 +249,37 @@ class Gpt : public Block::Partition_table * * \return the number of free blocks to the next logical entry */ - Genode::uint64_t _calculate_gap(Gpt_hdr const *header, - Gpt_entry const *entry, - Gpt_entry const *entries, - Genode::uint32_t num, - Genode::uint64_t total_blocks) + uint64_t _calculate_gap(Gpt_hdr const &header, + Gpt_entry const &entry, + Gpt_entry const &entries, + Genode::uint32_t num, + Genode::uint64_t total_blocks) { - using namespace Genode; - /* add one block => end == start */ - uint64_t const end_lba = entry->_lba_end + 1; + uint64_t const end_lba = entry.lba_end() + 1; enum { INVALID_START = ~0ull, }; uint64_t next_start_lba = INVALID_START; for (uint32_t i = 0; i < num; i++) { - Gpt_entry const *e = (entries + i); + Gpt_entry const e(entries.base() + i * header.entry_size()); - if (!e->valid() || e == entry) { continue; } + if (!e.valid() || e.base() == entry.base()) { continue; } /* * Check if the entry starts afterwards and save the * entry with the smallest distance. */ - if (e->_lba_start >= end_lba) { - next_start_lba = min(next_start_lba, e->_lba_start); + if (e.lba_start() >= end_lba) { + next_start_lba = min(next_start_lba, e.lba_start()); } } /* sanity check if GPT is broken */ - if (end_lba > header->_part_lba_end) { return 0; } + if (end_lba > header.part_lba_end()) { return 0; } /* if the underyling Block device changes we might be able to expand more */ - Genode::uint64_t const part_end = max(header->_part_lba_end, total_blocks); + uint64_t const part_end = max(header.part_lba_end(), total_blocks); /* * Use stored next start LBA or paritions end LBA from header, @@ -288,20 +299,18 @@ class Gpt : public Block::Partition_table * * \return the number of used blocks */ - Genode::uint64_t _calculate_used(Gpt_hdr const *, - Gpt_entry const *entries, - Genode::uint32_t num) + uint64_t _calculate_used(Gpt_hdr const &header, + Gpt_entry const &entries, + uint32_t num) { - using namespace Genode; - uint64_t used = 0; for (uint32_t i = 0; i < num; i++) { - Gpt_entry const *e = (entries + i); + Gpt_entry const e(entries.base() + i * header.entry_size()); - if (!e->valid()) { continue; } + if (!e.valid()) { continue; } - uint64_t const v = (e->_lba_end - e->_lba_start) + 1; + uint64_t const v = (e.lba_end() - e.lba_start()) + 1; used += v; } @@ -312,70 +321,76 @@ class Gpt : public Block::Partition_table /** * Parse the GPT header */ - void _parse_gpt(Gpt_hdr *gpt) + void _parse_gpt(Gpt_hdr &gpt) { - if (!(gpt->valid(driver))) - throw Genode::Exception(); + if (!(gpt.valid(data))) + throw Exception(); - Sector entry_array(driver, gpt->_gpe_lba, - gpt->_entries * gpt->_entry_size / driver.blk_size()); - Gpt_entry *entries = entry_array.addr(); + Sector entry_array(data, gpt.gpe_lba(), + gpt.entries() * gpt.entry_size() / block.info().block_size); + Gpt_entry entries(entry_array.addr()); for (int i = 0; i < MAX_PARTITIONS; i++) { - Gpt_entry *e = (entries + i); - if (!e->valid()) + Gpt_entry e(entries.base() + i * gpt.entry_size()); + + if (!e.valid()) continue; - Genode::uint64_t start = e->_lba_start; - Genode::uint64_t length = e->_lba_end - e->_lba_start + 1; /* [...) */ + uint64_t start = e.lba_start(); + uint64_t length = e.lba_end() - e.lba_start() + 1; /* [...) */ - _part_list[i] = new (&heap) Block::Partition(start, length); + _part_list[i].construct(start, length); - Genode::log("Partition ", i + 1, ": LBA ", start, " (", length, - " blocks) type: '", e->_type.to_string(), - "' name: '", e->name(), "'"); + log("Partition ", i + 1, ": LBA ", start, " (", length, + " blocks) type: '", e.type(), + "' name: '", e, "'"); } /* Report the partitions */ if (reporter.enabled()) { - Genode::Reporter::Xml_generator xml(reporter, [&] () { + Reporter::Xml_generator xml(reporter, [&] () { xml.attribute("type", "gpt"); - Genode::uint64_t const total_blocks = driver.blk_cnt(); + uint64_t const total_blocks = block.info().block_count; xml.attribute("total_blocks", total_blocks); - Genode::uint64_t const gpt_total = - (gpt->_part_lba_end - gpt->_part_lba_start) + 1; + uint64_t const gpt_total = + (gpt.part_lba_end() - gpt.part_lba_start()) + 1; xml.attribute("gpt_total", gpt_total); - Genode::uint64_t const gpt_used = - _calculate_used(gpt, entries, gpt->_entries); + uint64_t const gpt_used = + _calculate_used(gpt, entries, gpt.entries()); xml.attribute("gpt_used", gpt_used); for (int i = 0; i < MAX_PARTITIONS; i++) { - Gpt_entry *e = (entries + i); + Gpt_entry e(entries.base() + i * gpt.entry_size()); - if (!e->valid()){ + if (!e.valid()){ continue; } enum { BYTES = 4096, }; - Sector fs(driver, e->_lba_start, BYTES / driver.blk_size()); - Fs::Type fs_type = Fs::probe(fs.addr(), BYTES); + Sector fs(data, e.lba_start(), BYTES / block.info().block_size); + + Fs::Type fs_type = Fs::probe(fs.addr(), BYTES); + + String<40> guid { e.guid() }; + String<40> type { e.type() }; + String name { e }; xml.node("partition", [&] () { xml.attribute("number", i + 1); - xml.attribute("name", e->name()); - xml.attribute("type", e->_type.to_string()); - xml.attribute("guid", e->_guid.to_string()); - xml.attribute("start", e->_lba_start); - xml.attribute("length", e->_lba_end - e->_lba_start + 1); - xml.attribute("block_size", driver.blk_size()); + xml.attribute("name", name); + xml.attribute("type", type); + xml.attribute("guid", guid);; + xml.attribute("start", e.lba_start()); + xml.attribute("length", e.lba_end() - e.lba_start() + 1); + xml.attribute("block_size", block.info().block_size); - Genode::uint64_t const gap = _calculate_gap(gpt, e, entries, - gpt->_entries, + uint64_t const gap = _calculate_gap(gpt, e, entries, + gpt.entries(), total_blocks); if (gap) { xml.attribute("expandable", gap); } @@ -386,23 +401,35 @@ class Gpt : public Block::Partition_table } }); } - } public: using Partition_table::Partition_table; - Block::Partition *partition(int num) override { - return (num <= MAX_PARTITIONS && num > 0) ? _part_list[num-1] : 0; } + Partition &partition(long num) override + { + num -= 1; + + if (num < 0 || num > MAX_PARTITIONS) + throw -1; + + if (!_part_list[num].constructed()) + throw -1; + + return *_part_list[num]; + } bool parse() override { - Sector s(driver, Gpt_hdr::HEADER_LBA, 1); - _parse_gpt(s.addr()); + block.sigh(io_sigh); + + Sector s(data, Gpt_hdr::Hdr_lba::LBA, 1); + Gpt_hdr hdr(s.addr()); + _parse_gpt(hdr); for (unsigned num = 0; num < MAX_PARTITIONS; num++) - if (_part_list[num]) + if (_part_list[num].constructed()) return true; return false; } diff --git a/repos/os/src/server/part_block/main.cc b/repos/os/src/server/part_block/main.cc index bc0f311cb..a446be31d 100644 --- a/repos/os/src/server/part_block/main.cc +++ b/repos/os/src/server/part_block/main.cc @@ -7,66 +7,487 @@ */ /* - * Copyright (C) 2011-2017 Genode Labs GmbH + * Copyright (C) 2011-2020 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. */ #include +#include +#include +#include #include +#include +#include +#include -#include "component.h" -#include "driver.h" #include "gpt.h" #include "mbr.h" +namespace Block { + class Session_component; + struct Session_handler; + struct Dispatch; + class Main; -void Block::Driver::_ready_to_submit() { - Block::Session_component::wake_up(); } + template struct Job_queue; + + typedef Constructible Job_object; + using Response = Request_stream::Response; +}; -class Main +template +struct Block::Job_queue +{ + Job_object _jobs[ITEMS]; + Bit_allocator _alloc; + + addr_t alloc() + { + addr_t index = _alloc.alloc(); + return index; + } + + void free(addr_t index) + { + if (_jobs[index].constructed()) + _jobs[index].destruct(); + + _alloc.free(index); + } + + template + void with_job(addr_t index, FN const &fn) + { + fn(_jobs[index]); + } +}; + + +struct Block::Dispatch : Interface +{ + virtual Response submit(long number, Request const &request, addr_t addr) = 0; + virtual void update() = 0; + virtual void acknowledge_completed(bool all = true, long number = -1) = 0; + virtual Response sync(long number, Request const &request) = 0; +}; + + +struct Block::Session_handler : Interface +{ + Env &env; + Attached_ram_dataspace ds; + + Signal_handler request_handler + { env.ep(), *this, &Session_handler::handle }; + + Session_handler(Env &env, size_t buffer_size) + : env(env), ds(env.ram(), env.rm(), buffer_size) + { } + + virtual void handle_requests()= 0; + + void handle() + { + handle_requests(); + } +}; + + +class Block::Session_component : public Rpc_object, + public Session_handler, + public Block::Request_stream { private: - Block::Partition_table & _table(); - - Genode::Env &_env; - - Genode::Attached_rom_dataspace _config { _env, "config" }; - - Genode::Heap _heap { _env.ram(), _env.rm() }; - Block::Driver _driver { _env, _heap }; - Genode::Reporter _reporter { _env, "partitions" }; - Mbr_partition_table _mbr { _heap, _driver, _reporter }; - Gpt _gpt { _heap, _driver, _reporter }; - Block::Root _root { _env, _config.xml(), _heap, _driver, _table() }; + long _number; + Dispatch &_dispatcher; public: - struct No_partion_table : Genode::Exception { }; - struct Ambiguous_tables : Genode::Exception { }; - struct Invalid_config : Genode::Exception { }; + bool syncing { false }; - Main(Genode::Env &env) : _env(env) + Session_component(Env &env, long number, size_t buffer_size, + Session::Info info, Dispatch &dispatcher) + : Session_handler(env, buffer_size), + Request_stream(env.rm(), ds.cap(), env.ep(), request_handler, info), + _number(number), _dispatcher(dispatcher) { - /* - * we read all partition information, - * now it's safe to turn in asynchronous mode - */ - _driver.work_asynchronously(); + env.ep().manage(*this); + } - /* announce at parent */ - env.parent().announce(env.ep().manage(_root)); + ~Session_component() + { + env.ep().dissolve(*this); + } + + Info info() const override { return Request_stream::info(); } + + Capability tx_cap() override { return Request_stream::tx_cap(); } + + long number() const { return _number; } + + bool acknowledge(Request &request) + { + bool progress = false; + try_acknowledge([&] (Ack &ack) { + if (progress) return; + ack.submit(request); + progress = true; + }); + + return progress; + } + + void handle_requests() override + { + while (true) { + + bool progress = false; + + /* + * Acknowledge any pending packets before sending new request to the + * controller. + */ + _dispatcher.acknowledge_completed(false, _number); + + with_requests([&] (Request request) { + + Response response = Response::RETRY; + + if (syncing) return response; + + /* only READ/WRITE requests, others are noops for now */ + if (request.operation.type == Operation::Type::TRIM || + request.operation.type == Operation::Type::INVALID) { + request.success = true; + progress = true; + return Response::REJECTED; + } + + if (!info().writeable && request.operation.type == Operation::Type::WRITE) { + progress = true; + return Response::REJECTED; + } + + if (request.operation.type == Operation::Type::SYNC) { + response = _dispatcher.sync(_number, request); + if (response == Response::ACCEPTED) syncing = true; + return response; + } + + with_payload([&] (Request_stream::Payload const &payload) { + payload.with_content(request, [&] (void *addr, size_t) { + response = _dispatcher.submit(_number, request, addr_t(addr)); + }); + }); + + if (response != Response::RETRY) + progress = true; + + return response; + }); + + if (progress == false) break; + } + + _dispatcher.update(); + + /* poke */ + wakeup_client_if_needed(); } }; -Block::Partition_table & Main::_table() +class Block::Main : Rpc_object>, + Dispatch { - using namespace Genode; + private: + Partition_table & _table(); + + Env &_env; + + Attached_rom_dataspace _config { _env, "config" }; + + Heap _heap { _env.ram(), _env.rm() }; + Reporter _reporter { _env, "partitions" }; + + Number_of_bytes const _io_buffer_size = + _config.xml().attribute_value("io_buffer", + Number_of_bytes(4*1024*1024)); + + Allocator_avl _block_alloc { &_heap }; + Block_connection _block { _env, &_block_alloc, _io_buffer_size }; + Io_signal_handler
_io_sigh { _env.ep(), *this, &Main::_handle_io }; + Mbr_partition_table _mbr { _env, _block, _heap, _reporter }; + Gpt _gpt { _env, _block, _heap, _reporter }; + Partition_table &_partition_table { _table() }; + + enum { MAX_SESSIONS = 32 }; + Session_component *_sessions[MAX_SESSIONS] { }; + Job_queue<128> _job_queue { }; + Registry _job_registry { }; + + unsigned _wake_up_index { 0 }; + + void _wakeup_clients() + { + bool first = true; + unsigned next_index = 0; + for (long i = 0; i < MAX_SESSIONS; i++) { + + unsigned index = (_wake_up_index + i) % MAX_SESSIONS; + + if (!_sessions[index]) continue; + + if (_sessions[index]->syncing) { + bool in_flight = false; + + _job_registry.for_each([&] (Job &job) { + if (in_flight || i == job.number) { + Operation &op = job.request.operation; + in_flight |= (op.type == Operation::Type::WRITE || + op.type == Operation::Type::SYNC); + } + }); + + if (in_flight == false) _sessions[index]->syncing = false; + else continue; + } + + if (first) { + /* to be more fair, start at index + 1 next time */ + next_index = (index + 1) % MAX_SESSIONS; + first = false; + } + + _sessions[index]->handle_requests(); + } + + _wake_up_index = next_index; + } + + void _handle_io() + { + update(); + acknowledge_completed(); + _wakeup_clients(); + } + + Main(Main const &); + Main &operator = (Main const &); + + + public: + + struct No_partion_table : Exception { }; + struct Ambiguous_tables : Exception { }; + struct Invalid_config : Exception { }; + + Main(Env &env) : _env(env) + { + _block.sigh(_io_sigh); + + /* announce at parent */ + env.parent().announce(env.ep().manage(*this)); + } + + + /*********************** + ** Session interface ** + ***********************/ + + Genode::Session_capability session(Root::Session_args const &args, + Affinity const &) override + { + long num = -1; + bool writeable = false; + + Session_label const label = label_from_args(args.string()); + try { + Session_policy policy(label, _config.xml()); + + /* read partition attribute */ + num = policy.attribute_value("partition", -1L); + + /* sessions are not writeable by default */ + writeable = policy.attribute_value("writeable", false); + + } catch (Xml_node::Nonexistent_attribute) { + error("policy does not define partition number for for '", + label, "'"); + throw Service_denied(); + } catch (Session_policy::No_policy_defined) { + error("rejecting session request, no matching policy for '", + label, "'"); + throw Service_denied(); + } + + try { + _partition_table.partition(num); + } + catch (...) { + error("Partition ", num, " unavailable for '", label, "'"); + throw Service_denied(); + } + + if (num >= MAX_SESSIONS || _sessions[num]) { + error("Partition ", num, " already in use or session limit reached for '", + label, "'"); + throw Service_denied(); + } + + Ram_quota const ram_quota = ram_quota_from_args(args.string()); + size_t tx_buf_size = + Arg_string::find_arg(args.string(), "tx_buf_size").ulong_value(0); + + if (!tx_buf_size) + throw Service_denied(); + + /* delete ram quota by the memory needed for the session */ + size_t session_size = max((size_t)4096, + sizeof(Session_component)); + if (ram_quota.value < session_size) + throw Insufficient_ram_quota(); + + /* + * 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.value - session_size) { + error("insufficient 'ram_quota', got ", ram_quota, ", need ", + tx_buf_size + session_size); + throw Insufficient_ram_quota(); + } + + Session::Info info { + .block_size = _block.info().block_size, + .block_count = _partition_table.partition(num).sectors, + .align_log2 = 0, + .writeable = writeable, + }; + + _sessions[num] = new (_heap) Session_component(_env, num, tx_buf_size, + info, *this); + return _sessions[num]->cap(); + } + + void close(Genode::Session_capability cap) override + { + for (long number = 0; number < MAX_SESSIONS; number++) { + if (!_sessions[number] || !(cap == _sessions[number]->cap())) + continue; + + destroy(_heap, _sessions[number]); + _sessions[number] = nullptr; + + break; + } + } + + void upgrade(Genode::Session_capability, Root::Upgrade_args const&) override { } + + + /************************ + ** Update_jobs_policy ** + ************************/ + + void consume_read_result(Job &job, off_t, + char const *src, size_t length) + { + if (!_sessions[job.number]) return; + + memcpy((void *)(job.addr + job.offset), src, length); + job.offset += length; + } + + void produce_write_content(Job &job, off_t, char *dst, size_t length) + { + memcpy(dst, (void *)(job.addr + job.offset), length); + job.offset += length; + } + + void completed(Job &job, bool success) + { + job.request.success = success; + job.completed = true; + } + + + /************** + ** Dispatch ** + **************/ + + void update() override { _block.update_jobs(*this); } + + Response submit(long number, Request const &request, addr_t addr) override + { + Partition &partition = _partition_table.partition(number); + block_number_t last = request.operation.block_number + request.operation.count; + + if (last > partition.sectors) + return Response::REJECTED; + + addr_t index = 0; + try { + index = _job_queue.alloc(); + } catch (...) { return Response::RETRY; } + + _job_queue.with_job(index, [&](Job_object &job) { + + Operation op = request.operation; + op.block_number += partition.lba; + + job.construct(_block, op, _job_registry, index, number, request, addr); + }); + + return Response::ACCEPTED; + } + + Response sync(long number, Request const &request) override + { + addr_t index = 0; + try { + index = _job_queue.alloc(); + } catch (...) { return Response::RETRY; } + + _job_queue.with_job(index, [&](Job_object &job) { + job.construct(_block, request.operation, _job_registry, + index, number, request, 0); + }); + + return Response::ACCEPTED; + } + + void acknowledge_completed(bool all = true, long number = -1) override + { + _job_registry.for_each([&] (Job &job) { + if (!job.completed) return; + + addr_t index = job.index; + + /* free orphans */ + if (!_sessions[job.number]) { + _job_queue.free(index); + return; + } + + if (!all && job.number != number) + return; + + if (_sessions[job.number]->acknowledge(job.request)) + _job_queue.free(index); + }); + } +}; + + +Block::Partition_table & Block::Main::_table() +{ Xml_node const config = _config.xml(); bool const ignore_gpt = config.attribute_value("ignore_gpt", false); @@ -98,7 +519,7 @@ Block::Partition_table & Main::_table() try { valid_mbr = _mbr.parse(); } catch (Mbr_partition_table::Protective_mbr_found) { pmbr_found = true; - } + } catch (...) { }; } if (!ignore_gpt) { @@ -111,7 +532,6 @@ Block::Partition_table & Main::_table() * conjunction with a GPT header - hybrid operation is not supported) * and we will not decide which one to use, it is up to the user. */ - if (valid_mbr && valid_gpt) { error("ambigious tables: found valid MBR as well as valid GPT"); throw Ambiguous_tables(); @@ -137,5 +557,4 @@ Block::Partition_table & Main::_table() throw No_partion_table(); } - -void Component::construct(Genode::Env &env) { static Main main(env); } +void Component::construct(Genode::Env &env) { static Block::Main main(env); } diff --git a/repos/os/src/server/part_block/mbr.h b/repos/os/src/server/part_block/mbr.h index 3fa2e22ce..1bc448353 100644 --- a/repos/os/src/server/part_block/mbr.h +++ b/repos/os/src/server/part_block/mbr.h @@ -8,7 +8,7 @@ */ /* - * Copyright (C) 2013-2017 Genode Labs GmbH + * Copyright (C) 2013-2020 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. @@ -20,13 +20,17 @@ #include #include #include +#include #include "partition_table.h" #include "fsprobe.h" #include "ahdi.h" +namespace Block { + struct Mbr_partition_table; +}; -struct Mbr_partition_table : public Block::Partition_table +struct Block::Mbr_partition_table : public Block::Partition_table { public: @@ -39,67 +43,85 @@ struct Mbr_partition_table : public Block::Partition_table /** * Partition table entry format */ - struct Partition_record + struct Partition_record : Mmio { - enum { - INVALID = 0, - EXTENTED_CHS = 0x5, EXTENTED_LBA = 0xf, PROTECTIVE = 0xee + struct Type : Register<4, 8> + { + enum { + INVALID = 0, EXTENTED_CHS = 0x5, EXTENTED_LBA = 0xf, PROTECTIVE = 0xee + }; }; - 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 valid() const { return _type != INVALID; } - bool extended() const { return _type == EXTENTED_CHS - || _type == EXTENTED_LBA; } - bool protective() const { return _type == PROTECTIVE; } - } __attribute__((packed)); + struct Lba : Register<8, 32> { }; /* logical block address */ + struct Sectors : Register<12, 32> { }; /* number of sectors */ + Partition_record() = delete; + + Partition_record(addr_t base) + : Mmio(base) { } + + bool valid() const { return read() != Type::INVALID; } + bool extended() const { return read() == Type::EXTENTED_CHS || + read() == Type::EXTENTED_LBA; } + bool protective() const { return read() == Type::PROTECTIVE; } + uint8_t type() const { return read(); } + unsigned lba() const { return read(); } + unsigned sectors() const { return read(); } + + static constexpr size_t size() { return 16; } + }; /** * Master/Extented boot record format */ - struct Mbr + struct Mbr : Mmio { - Genode::uint8_t _unused[446]; - Partition_record _records[4]; - Genode::uint16_t _magic; + struct Magic : Register<510, 16> + { + enum { NUMBER = 0xaa55 }; + }; + + Mbr() = delete; + + Mbr(addr_t base) : Mmio(base) { } bool valid() const { /* magic number of partition table */ - enum { MAGIC = 0xaa55 }; - return _magic == MAGIC; + return read() == Magic::NUMBER; } - } __attribute__((packed)); + + addr_t record(unsigned index) const + { + return base() + 446 + (index * Partition_record::size()); + } + }; enum { MAX_PARTITIONS = 32 }; /* contains pointers to valid partitions or 0 */ - Block::Partition *_part_list[MAX_PARTITIONS]; + Constructible _part_list[MAX_PARTITIONS]; template void _parse_extended(Partition_record const &record, FUNC const &f) const { - Partition_record const *r = &record; - unsigned lba = r->_lba; + Reconstructible r(record.base()); + unsigned lba = r->lba(); unsigned last_lba = 0; /* first logical partition number */ int nr = 5; do { - Sector s(driver, lba, 1); - Mbr const &ebr = *s.addr(); + Sector s(const_cast(data), lba, 1); + Mbr const ebr(s.addr()); if (!ebr.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 const &logical = ebr._records[0]; + Partition_record const logical(ebr.record(0)); if (logical.valid() && nr < MAX_PARTITIONS) { f(nr++, logical, lba); } @@ -108,10 +130,11 @@ struct Mbr_partition_table : public Block::Partition_table * the second record points to the next EBR * (relative form this EBR) */ - r = &(ebr._records[1]); - lba += ebr._records[1]._lba - last_lba; + r.destruct(); + r.construct(ebr.record(1)); + lba += r->lba() - last_lba; - last_lba = ebr._records[1]._lba; + last_lba = r->lba(); } while (r->valid()); } @@ -120,7 +143,7 @@ struct Mbr_partition_table : public Block::Partition_table void _parse_mbr(Mbr const &mbr, FUNC const &f) const { for (int i = 0; i < 4; i++) { - Partition_record const &r = mbr._records[i]; + Partition_record const r(mbr.record(i)); if (!r.valid()) continue; @@ -139,59 +162,66 @@ struct Mbr_partition_table : public Block::Partition_table using Partition_table::Partition_table; - Block::Partition *partition(int num) override { - return (num < MAX_PARTITIONS) ? _part_list[num] : 0; } + Partition &partition(long num) override + { + if (num < 0 || num > MAX_PARTITIONS) + throw -1; + + if (!_part_list[num].constructed()) + throw -1; + + return *_part_list[num]; + } template void _for_each_valid_partition(FN const &fn) const { for (unsigned i = 0; i < MAX_PARTITIONS; i++) - if (_part_list[i]) + if (_part_list[i].constructed()) fn(i); }; bool parse() override { - using namespace Genode; + block.sigh(io_sigh); - Sector s(driver, 0, 1); + Sector s(data, 0, 1); /* check for MBR */ - Mbr const &mbr = *s.addr(); + Mbr const mbr(s.addr()); bool const mbr_valid = mbr.valid(); if (mbr_valid) { _parse_mbr(mbr, [&] (int i, Partition_record const &r, unsigned offset) { log("Partition ", i, ": LBA ", - (unsigned int) r._lba + offset, " (", - (unsigned int) r._sectors, " blocks) type: ", - Hex(r._type, Hex::OMIT_PREFIX)); + r.lba() + offset, " (", + r.sectors(), " blocks) type: ", + Hex(r.type(), Hex::OMIT_PREFIX)); if (!r.extended()) - _part_list[i] = new (heap) - Block::Partition(r._lba + offset, r._sectors); + _part_list[i].construct(Partition(r.lba() + offset, r.sectors())); }); } /* check for AHDI partition table */ bool const ahdi_valid = !mbr_valid && Ahdi::valid(s); if (ahdi_valid) - Ahdi::for_each_partition(s, [&] (unsigned i, Block::Partition info) { + Ahdi::for_each_partition(s, [&] (unsigned i, Partition info) { if (i < MAX_PARTITIONS) - _part_list[i] = new (heap) - Block::Partition(info.lba, info.sectors); }); + _part_list[i].construct( + Partition(info.lba, info.sectors)); }); /* no partition table, use whole disc as partition 0 */ if (!mbr_valid && !ahdi_valid) - _part_list[0] = new (&heap) - Block::Partition(0, driver.blk_cnt() - 1); + _part_list[0].construct( + Partition(0, block.info().block_count - 1)); /* report the partitions */ if (reporter.enabled()) { auto gen_partition_attr = [&] (Xml_generator &xml, unsigned i) { - Block::Partition const &part = *_part_list[i]; + Partition const &part = *_part_list[i]; - size_t const block_size = driver.blk_size(); + size_t const block_size = block.info().block_size; xml.attribute("number", i); xml.attribute("start", part.lba); @@ -200,7 +230,7 @@ struct Mbr_partition_table : public Block::Partition_table /* probe for known file-system types */ enum { PROBE_BYTES = 4096, }; - Sector fs(driver, part.lba, PROBE_BYTES / block_size); + Sector fs(data, part.lba, PROBE_BYTES / block_size); Fs::Type const fs_type = Fs::probe(fs.addr(), PROBE_BYTES); @@ -218,12 +248,12 @@ struct Mbr_partition_table : public Block::Partition_table _parse_mbr(mbr, [&] (int i, Partition_record const &r, unsigned) { /* nullptr if extended */ - if (!_part_list[i]) + if (!_part_list[i].constructed()) return; xml.node("partition", [&] { gen_partition_attr(xml, i); - xml.attribute("type", r._type); }); + xml.attribute("type", r.type()); }); }); } else if (ahdi_valid) { diff --git a/repos/os/src/server/part_block/partition_table.h b/repos/os/src/server/part_block/partition_table.h index 94d9ce02a..8ab4dbf98 100644 --- a/repos/os/src/server/part_block/partition_table.h +++ b/repos/os/src/server/part_block/partition_table.h @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2013-2017 Genode Labs GmbH + * Copyright (C) 2013-2020 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. @@ -17,68 +17,162 @@ #include #include -#include +#include +#include #include -#include "driver.h" - namespace Block { struct Partition; class Partition_table; + struct Job; + using namespace Genode; + typedef Block::Connection Block_connection; } struct Block::Partition { - Genode::uint64_t lba; /* logical block address on device */ - Genode::uint64_t sectors; /* number of sectors in patitions */ + block_number_t lba; /* logical block address on device */ + block_count_t sectors; /* number of sectors in patitions */ - Partition(Genode::uint64_t l, Genode::uint64_t s) - : lba(l), sectors(s) { } + Partition(block_number_t lba, block_count_t sectors) + : lba(lba), sectors(sectors) { } }; -struct Block::Partition_table : Genode::Interface +struct Block::Job : public Block_connection::Job { + Registry::Element registry_element; + + addr_t const index; /* job index */ + long const number; /* parition number */ + Request request; + addr_t const addr; /* target payload address */ + bool completed { false }; + off_t offset { 0 }; /* current offset in payload for partial jobs */ + + Job(Block_connection &connection, + Operation operation, + Registry ®istry, + addr_t const index, + addr_t const number, + Request request, + addr_t addr) + : Block_connection::Job(connection, operation), + registry_element(registry, *this), + index(index), number(number), request(request), addr(addr) { } +}; + + +struct Block::Partition_table : Interface +{ + struct Sector; + + struct Sector_data + { + Env &env; + Block_connection █ + Allocator &alloc; + Sector *current = nullptr; + + Sector_data(Env &env, Block_connection &block, Allocator &alloc) + : env(env), block(block), alloc(alloc) { } + }; + + /** + * Read sectors synchronously + */ class Sector { private: - Session_client &_session; - Packet_descriptor _p; + Sector_data &_data; + bool _completed { false }; + size_t _size { 0 }; + void *_buffer { nullptr }; + + Sector(Sector const &); + Sector &operator = (Sector const &); public: - Sector(Driver &driver, - unsigned long blk_nr, - unsigned long count, - bool write = false) - : _session(driver.session()), - _p(_session.alloc_packet(driver.blk_size() * count), - write ? Packet_descriptor::WRITE : Packet_descriptor::READ, - blk_nr, count) + Sector(Sector_data &data, + block_number_t block_number, + block_count_t count) + : _data(data) { - _session.tx()->submit_packet(_p); - _p = _session.tx()->get_acked_packet(); - if (!_p.succeeded()) - Genode::error("Could not access block ", - (unsigned long long)_p.block_number()); + Operation const operation { + .type = Operation::Type::READ, + .block_number = block_number, + .count = count + }; + + Block_connection::Job job { data.block, operation }; + _data.block.update_jobs(*this); + + _data.current = this; + + while (!_completed) + data.env.ep().wait_and_dispatch_one_io_signal(); + + _data.current = nullptr; } - ~Sector() { _session.tx()->release_packet(_p); } + ~Sector() + { + _data.alloc.free(_buffer, _size); + } + + void handle_io() + { + _data.block.update_jobs(*this); + } + + void consume_read_result(Block_connection::Job &, off_t, + char const *src, size_t length) + { + _buffer = _data.alloc.alloc(length); + memcpy(_buffer, src, length); + _size = length; + } + + void produce_write_content(Block_connection::Job &, off_t, char *, size_t) { } + + void completed(Block_connection::Job &, bool success) + { + _completed = true; + + if (!success) { + error("IO error during partition parsing"); + throw -1; + } + } template T addr() const { - return reinterpret_cast(_session.tx()->packet_content(_p)); } + return reinterpret_cast(_buffer); } }; - Genode::Heap & heap; - Driver & driver; - Genode::Reporter & reporter; + Env &env; + Block_connection █ + Reporter &reporter; + Sector_data data; - Partition_table(Genode::Heap & h, Driver & d, Genode::Reporter & r) - : heap(h), driver(d), reporter(r) {} + Io_signal_handler io_sigh { + env.ep(), *this, &Partition_table::handle_io }; - virtual Partition *partition(int num) = 0; + void handle_io() + { + if (data.current) { data.current->handle_io(); } + } + + Partition_table(Env &env, + Block_connection &block, + Allocator &alloc, + Reporter &r) + : env(env), block(block), reporter(r), data(env, block, alloc) + { } + + virtual Partition &partition(long num) = 0; virtual bool parse() = 0; };