part_block: switch to Request_stream and Job APIs

- use Job API as block connection back end
- use Request_stream API as front end
- use Mmio framework for gpt and mbr parsing
- implement sync correctly

fixes #3652
This commit is contained in:
Sebastian Sumpf 2020-02-10 17:13:21 +01:00 committed by Christian Helmuth
parent b95dc611d6
commit 3995d2f4a2
7 changed files with 876 additions and 860 deletions

View File

@ -70,8 +70,9 @@ Configuration snippet with two clients and an (hypothetical) IDE driver:
! </route>
!
! <!-- allow program 'test-part1' to access logical partition '6', while program
! 'test-part2' receives access to primary partition 1 -->
! <config>
! 'test-part2' receives access to primary partition 1, the buffer between
! the 'ata_driver' and 'part_block' is 1 MeB ('io_buffer') -->
! <config io_buffer="1M">
! <report partitions="yes"/>
! <policy label_prefix="test-part1" partition="6" writeable="yes"/>
! <policy label_prefix="test-part2" partition="1" writeable="yes"/>

View File

@ -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 <base/exception.h>
#include <base/component.h>
#include <os/session_policy.h>
#include <root/component.h>
#include <block_session/rpc_object.h>
#include "gpt.h"
namespace Block {
using namespace Genode;
class Session_component;
class Root;
};
class Block::Session_component : public Block::Session_rpc_object,
private List<Block::Session_component>::Element,
public Block_dispatcher
{
private:
friend class List<Block::Session_component>;
/*
* 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<Session_component> _sink_ack;
Signal_handler<Session_component> _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<Session_component>& wait_queue()
{
static List<Session_component> 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<Block::Session_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<Session_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_ */

View File

@ -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 <base/env.h>
#include <base/allocator_avl.h>
#include <base/signal.h>
#include <base/tslab.h>
#include <base/heap.h>
#include <util/list.h>
#include <block_session/connection.h>
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<Request>::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<Request, BLK_SZ> _r_slab;
Genode::List<Request> _r_list { };
Genode::Allocator_avl _block_alloc;
Block::Connection<> _session;
Block::Session::Info const _info { _session.info() };
Genode::Signal_handler<Driver> _source_ack;
Genode::Signal_handler<Driver> _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_ */

View File

@ -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 <base/log.h>
#include <block_session/client.h>
#include <util/misc_math.h>
#include <util/mmio.h>
#include <util/utf8.h>
#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<Genode::uint8_t const*>(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<Partition> _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<Time_low>(); }
template<typename T> struct Uuid_hex : Genode::Hex {
Uuid_hex<T>(T value) : Genode::Hex(value, OMIT_PREFIX, PAD) { } };
void print(Output &out) const
{
static char buffer[37 + 1];
Genode::print(out, Uuid_hex(read<Time_low>()),
"-", Uuid_hex(read<Time_mid>()),
"-", Uuid_hex(read<Time_hi_and_version>()),
"-", Uuid_hex(read<Clock_seq_hi_and_reserved>()),
Uuid_hex(read<Clock_seq_low>()),
"-");
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<Node>(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<Part_lba_start>(); }
uint64_t part_lba_end() const { return read<Part_lba_end>(); }
uint64_t gpe_lba() const { return read<Gpe_lba>(); }
uint32_t entries() const { return read<Entries>(); }
uint32_t entry_size() const { return read<Entry_size>(); }
uint32_t crc32(addr_t buf, size_t size)
{
uint8_t const *p = reinterpret_cast<uint8_t const*>(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<Revision>());
log(" size: ", read<Hdr_size>());
log(" crc: ", Hex(read<Hdr_crc>(), Hex::OMIT_PREFIX));
log(" reserved: ", read<Reserved>());
log(" hdr lba: ", read<Hdr_lba>());
log(" bak lba: ", read<Backup_hdr_lba>());
log(" part start lba: ", read<Part_lba_start>());
log(" part end lba: ", read<Part_lba_end>());
log(" guid: ", guid());
log(" gpe lba: ", read<Gpe_lba>());
log(" entries: ", read<Entries>());
log(" entry size: ", read<Entry_size>());
log(" gpe crc: ", Hex(read<Gpe_crc>(), 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<Sig>() != 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<Hdr_crc>();
write<Hdr_crc>(0);
if (crc32(base(), read<Hdr_size>()) != crc) {
error("Wrong GPT header checksum");
return false;
}
/* check header lba */
if (check_primary)
if (_hdr_lba != HEADER_LBA)
if (read<Hdr_lba>() != 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<void *>(), length) != _gpe_crc)
size_t length = entries() * entry_size();
Sector gpe(data, gpe_lba(), length / data.block.info().block_size);
if (crc32(gpe.addr<addr_t>(), length) != read<Gpe_crc>())
return false;
if (check_primary) {
/* check backup gpt header */
Sector backup_hdr(driver, _backup_hdr_lba, 1);
if (!backup_hdr.addr<Gpt_hdr*>()->valid(driver, false)) {
Genode::warning("Backup GPT header is corrupted");
Sector backup_hdr(data, read<Backup_hdr_lba>(), 1);
Gpt_hdr backup(backup_hdr.addr<addr_t>());
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<Lba_start>(); }
uint64_t lba_end() const { return read<Lba_end>(); }
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<Name>(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<Gpt_entry *>();
Sector entry_array(data, gpt.gpe_lba(),
gpt.entries() * gpt.entry_size() / block.info().block_size);
Gpt_entry entries(entry_array.addr<addr_t>());
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<Genode::uint8_t*>(), BYTES);
Sector fs(data, e.lba_start(), BYTES / block.info().block_size);
Fs::Type fs_type = Fs::probe(fs.addr<uint8_t*>(), BYTES);
String<40> guid { e.guid() };
String<40> type { e.type() };
String<Gpt_entry::NAME_LEN> 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<Gpt_hdr *>());
block.sigh(io_sigh);
Sector s(data, Gpt_hdr::Hdr_lba::LBA, 1);
Gpt_hdr hdr(s.addr<addr_t>());
_parse_gpt(hdr);
for (unsigned num = 0; num < MAX_PARTITIONS; num++)
if (_part_list[num])
if (_part_list[num].constructed())
return true;
return false;
}

View File

@ -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 <base/attached_rom_dataspace.h>
#include <base/attached_ram_dataspace.h>
#include <base/component.h>
#include <base/heap.h>
#include <block_session/rpc_object.h>
#include <block/request_stream.h>
#include <os/session_policy.h>
#include <util/bit_allocator.h>
#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 <unsigned ITEMS> struct Job_queue;
typedef Constructible<Job> Job_object;
using Response = Request_stream::Response;
};
class Main
template <unsigned ITEMS>
struct Block::Job_queue
{
Job_object _jobs[ITEMS];
Bit_allocator<ITEMS> _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<typename FN>
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<Session_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<Block::Session>,
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> 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<Typed_root<Session>>,
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<Main> _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<Block::Job> _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); }

View File

@ -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 <base/env.h>
#include <base/log.h>
#include <block_session/client.h>
#include <util/mmio.h>
#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>() != Type::INVALID; }
bool extended() const { return read<Type>() == Type::EXTENTED_CHS ||
read<Type>() == Type::EXTENTED_LBA; }
bool protective() const { return read<Type>() == Type::PROTECTIVE; }
uint8_t type() const { return read<Type>(); }
unsigned lba() const { return read<Lba>(); }
unsigned sectors() const { return read<Sectors>(); }
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>() == 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<Partition> _part_list[MAX_PARTITIONS];
template <typename FUNC>
void _parse_extended(Partition_record const &record, FUNC const &f) const
{
Partition_record const *r = &record;
unsigned lba = r->_lba;
Reconstructible<Partition_record const> 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<Mbr *>();
Sector s(const_cast<Sector_data&>(data), lba, 1);
Mbr const ebr(s.addr<addr_t>());
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 <typename FN>
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 *>();
Mbr const mbr(s.addr<addr_t>());
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<uint8_t*>(), 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) {

View File

@ -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 <base/env.h>
#include <base/log.h>
#include <block_session/client.h>
#include <base/registry.h>
#include <block_session/connection.h>
#include <os/reporter.h>
#include "driver.h"
namespace Block {
struct Partition;
class Partition_table;
struct Job;
using namespace Genode;
typedef Block::Connection<Job> 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<Job>::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<Job> &registry,
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 &block;
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 <typename T> T addr() const {
return reinterpret_cast<T>(_session.tx()->packet_content(_p)); }
return reinterpret_cast<T>(_buffer); }
};
Genode::Heap & heap;
Driver & driver;
Genode::Reporter & reporter;
Env &env;
Block_connection &block;
Reporter &reporter;
Sector_data data;
Partition_table(Genode::Heap & h, Driver & d, Genode::Reporter & r)
: heap(h), driver(d), reporter(r) {}
Io_signal_handler<Partition_table> 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;
};