mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-21 00:23:16 +00:00
Consistently name block components
This patch replaces abbreviations like "blk", "cli", and "srv" by their full forms "block", "client", and "server". Fixes #1258
This commit is contained in:
96
repos/os/src/server/part_block/README
Normal file
96
repos/os/src/server/part_block/README
Normal file
@ -0,0 +1,96 @@
|
||||
This directory contains an implementation of a block-device-partition server.
|
||||
|
||||
Behavior
|
||||
--------
|
||||
|
||||
The server uses Genode's block-session interfaces as both front and back end,
|
||||
leading to the most common use case where this server will reside "between" a
|
||||
block-driver server and a higher level component like a file-system server.
|
||||
|
||||
At startup, the partition server will try to parse the master boot record (MBR)
|
||||
of its back-end block session. If no partition table is found, the whole block
|
||||
device is exported as partition '0'. In the other case, the MBR and possible
|
||||
extended boot records (EBRs) are parsed and offered as separate block sessions
|
||||
to the front-end clients. The four primary partitions will receive partition
|
||||
numbers '1' to '4' whereas the first logical partition will be assigned to '5'.
|
||||
|
||||
The partition server also understands the GUID partition table (GPT). It will
|
||||
always try to read the MBR as well as the GPT and will bail out if both are
|
||||
considered valid. It is up to the user to choose the right table. To do so,
|
||||
the 'ignore_mbr' or 'ignore_gpt' config attribute may be specified. Using both
|
||||
at the same time is a configuration error. Apart from that, the server will
|
||||
show a warning in case a protective MBR is found but GPT is ignored and abort.
|
||||
If valid GPT was encountered without a proper protective MBR it will use the
|
||||
GPT but show a diagnostic warning.
|
||||
|
||||
|
||||
In order to route a client to the right partition, the server parses its
|
||||
configuration section looking for 'policy' tags.
|
||||
|
||||
XML Syntax:
|
||||
! <policy label="<program name>" partition="<partition number>" writeable="<boolean>"/>
|
||||
|
||||
part_block supports partition reporting, which can be enabled via the
|
||||
<report> configuration node. See below for an example. The report
|
||||
looks like follows (for MBR resp. GPT).
|
||||
|
||||
! <partitions type="mbr">
|
||||
! <partition number="1" type="12" start="2048" length="2048"/>
|
||||
! <partition number="2" type="15" start="4096" length="16384"/>
|
||||
! <partition number="5" type="12" start="6144" length="4096"/>
|
||||
! <partition number="6" type="12" start="12288" length="8192"/>
|
||||
! </partitions>
|
||||
! <partitions type="gpt">
|
||||
! <partition number="1" name="one" type="ebd0a0a2-b9e5-4433-87c0-68b6b72699c7"
|
||||
! guid="5f4061cc-8d4a-4e6f-ad15-10b881b79aee" start="2048" length="2048"/>
|
||||
! <partition number="2" name="two" type="ebd0a0a2-b9e5-4433-87c0-68b6b72699c7"
|
||||
! guid="87199a83-d0f4-4a01-b9e3-6516a8579d61" start="4096" length="16351"/>
|
||||
! </partitions>
|
||||
|
||||
Clients have read-only access to partitions unless overriden by a 'writeable'
|
||||
policy attribute.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Configuration snippet with two clients and an (hypothetical) IDE driver:
|
||||
!<start name="ata_driver">
|
||||
! <resource name="RAM" quantum="1M" />
|
||||
! <provides><service name="Block"/></provides>
|
||||
! <config ata="yes" />
|
||||
!</start>
|
||||
!
|
||||
!<start name="part_block">
|
||||
! <resource name="RAM" quantum="10M" />
|
||||
! <provides><service name="Block" /></provides>
|
||||
!
|
||||
! <!-- route part_block to the ata_driver -->
|
||||
! <route>
|
||||
! <any-service><child name="ata_driver"/> <parent/><any-child/></any-service>
|
||||
! </route>
|
||||
!
|
||||
! <!-- allow program 'test-part1' to access logical partition '6', while program
|
||||
! 'test-part2' receives access to primary partition 1 -->
|
||||
! <config>
|
||||
! <report partitions="yes"/>
|
||||
! <policy label_prefix="test-part1" partition="6" writeable="yes"/>
|
||||
! <policy label_prefix="test-part2" partition="1" writeable="yes"/>
|
||||
! </config>
|
||||
!</start>
|
||||
!
|
||||
!<!-- part_block clients -->
|
||||
!<start name="test-part1">
|
||||
! <binary name="test-part"/>
|
||||
! <resource name="RAM" quantum="10M" />
|
||||
! <route>
|
||||
! <any-service> <child name="part_block" /> <parent/> <any-child/> </any-service>
|
||||
! </route>
|
||||
!</start>
|
||||
!
|
||||
!<start name="test-part2">
|
||||
! <binary name="test-part"/>
|
||||
! <resource name="RAM" quantum="10M" />
|
||||
! <route>
|
||||
! <any-service> <child name="part_block" /> <parent/> <any-child/> </any-service>
|
||||
! </route>
|
||||
!</start>
|
344
repos/os/src/server/part_block/component.h
Normal file
344
repos/os/src/server/part_block/component.h
Normal file
@ -0,0 +1,344 @@
|
||||
/*
|
||||
* \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;
|
||||
}
|
||||
|
||||
bool write = _p_to_handle.operation() == Packet_descriptor::WRITE;
|
||||
sector_t off = _p_to_handle.block_number() + _partition->lba;
|
||||
size_t cnt = _p_to_handle.block_count();
|
||||
|
||||
if (write && !_writeable) {
|
||||
_ack_packet(_p_to_handle);
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
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 **
|
||||
*******************************/
|
||||
|
||||
void info(sector_t *blk_count, size_t *blk_size,
|
||||
Operations *ops)
|
||||
{
|
||||
Operations driver_ops = _driver.ops();
|
||||
|
||||
*blk_count = _partition->sectors;
|
||||
*blk_size = _driver.blk_size();
|
||||
*ops = Operations();
|
||||
|
||||
typedef Block::Packet_descriptor::Opcode Opcode;
|
||||
|
||||
if (driver_ops.supported(Opcode::READ))
|
||||
ops->set_operation(Opcode::READ);
|
||||
if (_writeable && driver_ops.supported(Opcode::WRITE))
|
||||
ops->set_operation(Opcode::WRITE);
|
||||
}
|
||||
|
||||
void sync() { _driver.session().sync(); }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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 */
|
||||
policy.attribute("partition").value(&num);
|
||||
|
||||
/* 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_ */
|
174
repos/os/src/server/part_block/driver.h
Normal file
174
repos/os/src/server/part_block/driver.h
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* \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.operation() == p2.operation() &&
|
||||
p1.block_number() == p2.block_number() &&
|
||||
p1.block_count() == p2.block_count();
|
||||
}
|
||||
|
||||
|
||||
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 &cli,
|
||||
Packet_descriptor &srv)
|
||||
: _dispatcher(d), _cli(cli), _srv(srv) {}
|
||||
|
||||
bool handle(Packet_descriptor& reply)
|
||||
{
|
||||
bool ret = reply == _srv;
|
||||
if (ret) _dispatcher.dispatch(_cli, reply);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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::sector_t _blk_cnt = 0;
|
||||
Genode::size_t _blk_size = 0;
|
||||
Genode::Signal_handler<Driver> _source_ack;
|
||||
Genode::Signal_handler<Driver> _source_submit;
|
||||
Block::Session::Operations _ops { };
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
_session.info(&_blk_cnt, &_blk_size, &_ops);
|
||||
}
|
||||
|
||||
Genode::size_t blk_size() { return _blk_size; }
|
||||
Genode::size_t blk_cnt() { return _blk_cnt; }
|
||||
Session::Operations ops() { return _ops; }
|
||||
Session_client& session() { return _session; }
|
||||
|
||||
void work_asynchronously()
|
||||
{
|
||||
_session.tx_channel()->sigh_ack_avail(_source_ack);
|
||||
_session.tx_channel()->sigh_ready_to_submit(_source_submit);
|
||||
}
|
||||
|
||||
static Driver& driver();
|
||||
|
||||
void io(bool write, sector_t nr, Genode::size_t cnt, void* addr,
|
||||
Block_dispatcher &dispatcher, Packet_descriptor& cli)
|
||||
{
|
||||
if (!_session.tx()->ready_to_submit())
|
||||
throw Block::Session::Tx::Source::Packet_alloc_failed();
|
||||
|
||||
Block::Packet_descriptor::Opcode op = write
|
||||
? Block::Packet_descriptor::WRITE
|
||||
: Block::Packet_descriptor::READ;
|
||||
Genode::size_t size = _blk_size * cnt;
|
||||
Packet_descriptor p(_session.dma_alloc_packet(size),
|
||||
op, nr, cnt);
|
||||
Request *r = new (&_r_slab) Request(dispatcher, cli, p);
|
||||
_r_list.insert(r);
|
||||
|
||||
if (write)
|
||||
Genode::memcpy(_session.tx()->packet_content(p),
|
||||
addr, size);
|
||||
|
||||
_session.tx()->submit_packet(p);
|
||||
}
|
||||
|
||||
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_ */
|
104
repos/os/src/server/part_block/fsprobe.h
Normal file
104
repos/os/src/server/part_block/fsprobe.h
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* \brief Poor man's partition probe for known file system
|
||||
* \author Josef Soentgen
|
||||
* \date 2018-05-03
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 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__FSPROBE_H_
|
||||
#define _PART_BLOCK__FSPROBE_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/env.h>
|
||||
#include <base/log.h>
|
||||
|
||||
namespace Fs {
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
using Type = Genode::String<32>;
|
||||
|
||||
Type probe(uint8_t *buffer, size_t len);
|
||||
|
||||
/**
|
||||
* Probe for Ext2/3/4
|
||||
*/
|
||||
Type _probe_extfs(uint8_t *p, size_t len)
|
||||
{
|
||||
if (len < 4096) { return Type(); }
|
||||
|
||||
/* super block starts a byte offset 1024 */
|
||||
p += 0x400;
|
||||
|
||||
bool const found_ext_sig = p[0x38] == 0x53 && p[0x39] == 0xEF;
|
||||
|
||||
enum {
|
||||
COMPAT_HAS_JOURNAL = 0x004u,
|
||||
INCOMPAT_EXTENTS = 0x040u,
|
||||
RO_COMPAT_METADATA_CSUM = 0x400u,
|
||||
};
|
||||
|
||||
uint32_t const compat = *(uint32_t*)(p + 0x5C);
|
||||
uint32_t const incompat = *(uint32_t*)(p + 0x60);
|
||||
uint32_t const ro_compat = *(uint32_t*)(p + 0x64);
|
||||
|
||||
/* the feature flags should denote a given Ext version */
|
||||
|
||||
bool const ext3 = compat & COMPAT_HAS_JOURNAL;
|
||||
bool const ext4 = ext3 && ((incompat & INCOMPAT_EXTENTS)
|
||||
&& (ro_compat & RO_COMPAT_METADATA_CSUM));
|
||||
|
||||
if (found_ext_sig && ext4) { return Type("Ext4"); }
|
||||
else if (found_ext_sig && ext3) { return Type("Ext3"); }
|
||||
else if (found_ext_sig) { return Type("Ext2"); }
|
||||
|
||||
return Type();
|
||||
}
|
||||
|
||||
/**
|
||||
* Probe for FAT16/32
|
||||
*/
|
||||
Type _probe_fatfs(uint8_t *p, size_t len)
|
||||
{
|
||||
if (len < 512) { return Type(); }
|
||||
|
||||
/* at least the checks ring true when mkfs.vfat is used... */
|
||||
|
||||
bool const found_boot_sig = p[510] == 0x55 && p[511] == 0xAA;
|
||||
|
||||
bool const fat16 = p[38] == 0x28 || p[38] == 0x29;
|
||||
bool const fat32 = (p[66] == 0x28 || p[66] == 0x29)
|
||||
&& (p[82] == 'F' && p[83] == 'A');
|
||||
|
||||
if (found_boot_sig && fat32) { return Type("FAT32"); }
|
||||
if (found_boot_sig && fat16) { return Type("FAT16"); }
|
||||
|
||||
return Type();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Fs::Type Fs::probe(uint8_t *p, size_t len)
|
||||
{
|
||||
/* Ext2/3/4 */
|
||||
{
|
||||
Type t = _probe_extfs(p, len);
|
||||
if (t.valid()) { return t; }
|
||||
}
|
||||
|
||||
/* FAT16/32 */
|
||||
{
|
||||
Type t = _probe_fatfs(p, len);
|
||||
if (t.valid()) { return t; }
|
||||
}
|
||||
|
||||
return Type();
|
||||
}
|
||||
|
||||
#endif /* _PART_BLOCK__FSPROBE_H_ */
|
411
repos/os/src/server/part_block/gpt.h
Normal file
411
repos/os/src/server/part_block/gpt.h
Normal file
@ -0,0 +1,411 @@
|
||||
/*
|
||||
* \brief GUID Partition table definitions
|
||||
* \author Josef Soentgen
|
||||
* \date 2014-09-19
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014-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__GPT_H_
|
||||
#define _PART_BLOCK__GPT_H_
|
||||
|
||||
#include <base/env.h>
|
||||
#include <base/log.h>
|
||||
#include <block_session/client.h>
|
||||
#include <util/misc_math.h>
|
||||
|
||||
#include "driver.h"
|
||||
#include "partition_table.h"
|
||||
#include "fsprobe.h"
|
||||
|
||||
namespace {
|
||||
|
||||
static bool const verbose = false;
|
||||
|
||||
/* 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
|
||||
{
|
||||
private:
|
||||
|
||||
enum { MAX_PARTITIONS = 128 };
|
||||
|
||||
/* contains pointers to valid partitions or 0 */
|
||||
Block::Partition *_part_list[MAX_PARTITIONS] { 0 };
|
||||
|
||||
typedef Block::Partition_table::Sector Sector;
|
||||
|
||||
|
||||
/**
|
||||
* DCE uuid struct
|
||||
*/
|
||||
struct Uuid
|
||||
{
|
||||
enum { UUID_NODE_LEN = 6 };
|
||||
|
||||
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];
|
||||
|
||||
char const *to_string()
|
||||
{
|
||||
static char buffer[37 + 1];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
/**
|
||||
* GUID parition table header
|
||||
*/
|
||||
struct Gpt_hdr
|
||||
{
|
||||
enum { HEADER_LBA = 1 };
|
||||
|
||||
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 */
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
bool valid(Block::Driver &driver, bool check_primary = true)
|
||||
{
|
||||
dump_hdr(check_primary);
|
||||
|
||||
/* check sig */
|
||||
if (Genode::strcmp(_sig, "EFI PART", 8) != 0)
|
||||
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");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* check header lba */
|
||||
if (check_primary)
|
||||
if (_hdr_lba != HEADER_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)
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* the remainder of the LBA must be zero */
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
/**
|
||||
* GUID partition entry format
|
||||
*/
|
||||
struct Gpt_entry
|
||||
{
|
||||
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 */
|
||||
|
||||
bool valid() const
|
||||
{
|
||||
if (_type.time_low == 0x00000000)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract all valid ASCII characters in the name entry
|
||||
*/
|
||||
char const *name()
|
||||
{
|
||||
static char buffer[NAME_LEN + 1];
|
||||
|
||||
char *p = buffer;
|
||||
Genode::size_t i = 0;
|
||||
|
||||
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 : '.';
|
||||
}
|
||||
|
||||
*p++ = 0;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
/**
|
||||
* Calculate free blocks until the start of the logical next entry
|
||||
*
|
||||
* \param header pointer to GPT header
|
||||
* \param entry pointer to current entry
|
||||
* \param entries pointer to entries
|
||||
* \param num number of entries
|
||||
*
|
||||
* \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)
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
/* add one block => end == start */
|
||||
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);
|
||||
|
||||
if (!e->valid() || e == entry) { 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);
|
||||
}
|
||||
}
|
||||
|
||||
/* sanity check if GPT is broken */
|
||||
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);
|
||||
|
||||
/*
|
||||
* Use stored next start LBA or paritions end LBA from header,
|
||||
* if there is no other entry or we are the only one.
|
||||
*/
|
||||
return (next_start_lba == INVALID_START ? part_end
|
||||
: next_start_lba) - end_lba;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate total used blocks
|
||||
*
|
||||
* \param header pointer to GPT header
|
||||
* \param entries pointer to entries
|
||||
* \param num number of entries
|
||||
*
|
||||
* \return the number of used blocks
|
||||
*/
|
||||
Genode::uint64_t _calculate_used(Gpt_hdr const *,
|
||||
Gpt_entry const *entries,
|
||||
Genode::uint32_t num)
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
uint64_t used = 0;
|
||||
|
||||
for (uint32_t i = 0; i < num; i++) {
|
||||
Gpt_entry const *e = (entries + i);
|
||||
|
||||
if (!e->valid()) { continue; }
|
||||
|
||||
uint64_t const v = (e->_lba_end - e->_lba_start) + 1;
|
||||
used += v;
|
||||
}
|
||||
|
||||
return used;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse the GPT header
|
||||
*/
|
||||
void _parse_gpt(Gpt_hdr *gpt)
|
||||
{
|
||||
if (!(gpt->valid(driver)))
|
||||
throw Genode::Exception();
|
||||
|
||||
Sector entry_array(driver, gpt->_gpe_lba,
|
||||
gpt->_entries * gpt->_entry_size / driver.blk_size());
|
||||
Gpt_entry *entries = entry_array.addr<Gpt_entry *>();
|
||||
|
||||
for (int i = 0; i < MAX_PARTITIONS; i++) {
|
||||
Gpt_entry *e = (entries + i);
|
||||
|
||||
if (!e->valid())
|
||||
continue;
|
||||
|
||||
Genode::uint64_t start = e->_lba_start;
|
||||
Genode::uint64_t length = e->_lba_end - e->_lba_start + 1; /* [...) */
|
||||
|
||||
_part_list[i] = new (&heap) Block::Partition(start, length);
|
||||
|
||||
Genode::log("Partition ", i + 1, ": LBA ", start, " (", length,
|
||||
" blocks) type: '", e->_type.to_string(),
|
||||
"' name: '", e->name(), "'");
|
||||
}
|
||||
|
||||
/* Report the partitions */
|
||||
if (reporter.enabled())
|
||||
{
|
||||
Genode::Reporter::Xml_generator xml(reporter, [&] () {
|
||||
xml.attribute("type", "gpt");
|
||||
|
||||
Genode::uint64_t const total_blocks = driver.blk_cnt();
|
||||
xml.attribute("total_blocks", total_blocks);
|
||||
|
||||
Genode::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);
|
||||
xml.attribute("gpt_used", gpt_used);
|
||||
|
||||
for (int i = 0; i < MAX_PARTITIONS; i++) {
|
||||
Gpt_entry *e = (entries + i);
|
||||
|
||||
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);
|
||||
|
||||
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());
|
||||
|
||||
Genode::uint64_t const gap = _calculate_gap(gpt, e, entries,
|
||||
gpt->_entries,
|
||||
total_blocks);
|
||||
if (gap) { xml.attribute("expandable", gap); }
|
||||
|
||||
if (fs_type.valid()) {
|
||||
xml.attribute("file_system", fs_type);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
using Partition_table::Partition_table;
|
||||
|
||||
Block::Partition *partition(int num) {
|
||||
return (num <= MAX_PARTITIONS && num > 0) ? _part_list[num-1] : 0; }
|
||||
|
||||
bool parse()
|
||||
{
|
||||
Sector s(driver, Gpt_hdr::HEADER_LBA, 1);
|
||||
_parse_gpt(s.addr<Gpt_hdr *>());
|
||||
|
||||
for (unsigned num = 0; num < MAX_PARTITIONS; num++)
|
||||
if (_part_list[num])
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _PART_BLOCK__GUID_PARTITION_TABLE_H_ */
|
143
repos/os/src/server/part_block/main.cc
Normal file
143
repos/os/src/server/part_block/main.cc
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* \brief Front end of the partition server
|
||||
* \author Sebastian Sumpf
|
||||
* \author Stefan Kalkowski
|
||||
* \author Josef Soentgen
|
||||
* \date 2011-05-30
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-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.
|
||||
*/
|
||||
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <block_session/rpc_object.h>
|
||||
|
||||
#include "component.h"
|
||||
#include "driver.h"
|
||||
#include "gpt.h"
|
||||
#include "mbr.h"
|
||||
|
||||
|
||||
void Block::Driver::_ready_to_submit() {
|
||||
Block::Session_component::wake_up(); }
|
||||
|
||||
|
||||
class Main
|
||||
{
|
||||
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() };
|
||||
|
||||
public:
|
||||
|
||||
struct No_partion_table : Genode::Exception { };
|
||||
struct Ambiguous_tables : Genode::Exception { };
|
||||
struct Invalid_config : Genode::Exception { };
|
||||
|
||||
Main(Genode::Env &env) : _env(env)
|
||||
{
|
||||
/*
|
||||
* we read all partition information,
|
||||
* now it's safe to turn in asynchronous mode
|
||||
*/
|
||||
_driver.work_asynchronously();
|
||||
|
||||
/* announce at parent */
|
||||
env.parent().announce(env.ep().manage(_root));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Block::Partition_table & Main::_table()
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
bool valid_mbr = false;
|
||||
bool valid_gpt = false;
|
||||
bool ignore_gpt = false;
|
||||
bool ignore_mbr = false;
|
||||
bool pmbr_found = false;
|
||||
bool report = false;
|
||||
|
||||
try {
|
||||
ignore_gpt = _config.xml().attribute_value("ignore_gpt", false);
|
||||
ignore_mbr = _config.xml().attribute_value("ignore_mbr", false);
|
||||
} catch(...) {}
|
||||
|
||||
if (ignore_gpt && ignore_mbr) {
|
||||
error("invalid configuration: cannot ignore GPT as well as MBR");
|
||||
throw Invalid_config();
|
||||
}
|
||||
|
||||
try {
|
||||
report = _config.xml().sub_node("report").attribute_value
|
||||
("partitions", false);
|
||||
if (report)
|
||||
_reporter.enabled(true);
|
||||
} catch(...) {}
|
||||
|
||||
/*
|
||||
* Try to parse MBR as well as GPT first if not instructued
|
||||
* to ignore either one of them.
|
||||
*/
|
||||
|
||||
if (!ignore_mbr) {
|
||||
try { valid_mbr = _mbr.parse(); }
|
||||
catch (Mbr_partition_table::Protective_mbr_found) {
|
||||
pmbr_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ignore_gpt) {
|
||||
try { valid_gpt = _gpt.parse(); }
|
||||
catch (...) { }
|
||||
}
|
||||
|
||||
/*
|
||||
* Both tables are valid (although we would have expected a PMBR in
|
||||
* 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();
|
||||
}
|
||||
|
||||
if (valid_gpt && !pmbr_found) {
|
||||
warning("will use GPT without proper protective MBR");
|
||||
}
|
||||
|
||||
/* PMBR missing, i.e, MBR part[0] contains whole disk and GPT valid */
|
||||
if (pmbr_found && ignore_gpt) {
|
||||
warning("found protective MBR but GPT is to be ignored");
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the appropriate table or abort if none is found.
|
||||
*/
|
||||
|
||||
if (valid_gpt) return _gpt;
|
||||
if (valid_mbr) return _mbr;
|
||||
|
||||
error("Aborting: no partition table found.");
|
||||
throw No_partion_table();
|
||||
}
|
||||
|
||||
|
||||
void Component::construct(Genode::Env &env) { static Main main(env); }
|
222
repos/os/src/server/part_block/mbr.h
Normal file
222
repos/os/src/server/part_block/mbr.h
Normal file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* \brief MBR partition table definitions
|
||||
* \author Sebastian Sumpf
|
||||
* \author Stefan Kalkowski
|
||||
* \author Josef Soentgen
|
||||
* \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__MBR_H_
|
||||
#define _PART_BLOCK__MBR_H_
|
||||
|
||||
#include <base/env.h>
|
||||
#include <base/log.h>
|
||||
#include <block_session/client.h>
|
||||
|
||||
#include "partition_table.h"
|
||||
#include "fsprobe.h"
|
||||
|
||||
|
||||
struct Mbr_partition_table : public Block::Partition_table
|
||||
{
|
||||
public:
|
||||
|
||||
class Protective_mbr_found { };
|
||||
|
||||
private:
|
||||
|
||||
typedef Block::Partition_table::Sector Sector;
|
||||
|
||||
/**
|
||||
* Partition table entry format
|
||||
*/
|
||||
struct Partition_record
|
||||
{
|
||||
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() { return _type != INVALID; }
|
||||
bool extended() { return _type == EXTENTED_CHS
|
||||
|| _type == EXTENTED_LBA; }
|
||||
bool protective() { return _type == PROTECTIVE; }
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
/**
|
||||
* Master/Extented boot record format
|
||||
*/
|
||||
struct Mbr
|
||||
{
|
||||
Genode::uint8_t _unused[446];
|
||||
Partition_record _records[4];
|
||||
Genode::uint16_t _magic;
|
||||
|
||||
bool valid()
|
||||
{
|
||||
/* magic number of partition table */
|
||||
enum { MAGIC = 0xaa55 };
|
||||
return _magic == MAGIC;
|
||||
}
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
enum { MAX_PARTITIONS = 32 };
|
||||
|
||||
Block::Partition *_part_list[MAX_PARTITIONS]; /* contains pointers to valid
|
||||
partitions or 0 */
|
||||
|
||||
template <typename FUNC>
|
||||
void _parse_extended(Partition_record *record, FUNC const &f)
|
||||
{
|
||||
Partition_record *r = record;
|
||||
unsigned lba = r->_lba;
|
||||
|
||||
/* first logical partition number */
|
||||
int nr = 5;
|
||||
do {
|
||||
Sector s(driver, lba, 1);
|
||||
Mbr *ebr = s.addr<Mbr *>();
|
||||
|
||||
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 *logical = &(ebr->_records[0]);
|
||||
if (logical->valid() && nr < MAX_PARTITIONS) {
|
||||
f(nr++, logical, lba);
|
||||
}
|
||||
|
||||
/*
|
||||
* the second record points to the next EBR
|
||||
* (relative form this EBR)
|
||||
*/
|
||||
r = &(ebr->_records[1]);
|
||||
lba += ebr->_records[1]._lba;
|
||||
|
||||
} while (r->valid());
|
||||
}
|
||||
|
||||
template <typename FUNC>
|
||||
void _parse_mbr(Mbr *mbr, FUNC const &f)
|
||||
{
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Partition_record *r = &(mbr->_records[i]);
|
||||
|
||||
if (!r->valid())
|
||||
continue;
|
||||
|
||||
if (r->protective())
|
||||
throw Protective_mbr_found();
|
||||
|
||||
f(i + 1, r, 0);
|
||||
|
||||
if (r->extended()) {
|
||||
_parse_extended(r, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
using Partition_table::Partition_table;
|
||||
|
||||
Block::Partition *partition(int num) {
|
||||
return (num < MAX_PARTITIONS) ? _part_list[num] : 0; }
|
||||
|
||||
bool parse()
|
||||
{
|
||||
Sector s(driver, 0, 1);
|
||||
Mbr *mbr = s.addr<Mbr *>();
|
||||
|
||||
/* no partition table, use whole disc as partition 0 */
|
||||
bool const mbr_valid = mbr->valid();
|
||||
if (!mbr_valid) {
|
||||
_part_list[0] = new (&heap)
|
||||
Block::Partition(0, driver.blk_cnt() - 1);
|
||||
} else {
|
||||
_parse_mbr(mbr, [&] (int i, Partition_record *r, unsigned offset) {
|
||||
Genode::log("Partition ", i, ": LBA ",
|
||||
(unsigned int) r->_lba + offset, " (",
|
||||
(unsigned int) r->_sectors, " blocks) type: ",
|
||||
Genode::Hex(r->_type, Genode::Hex::OMIT_PREFIX));
|
||||
if (!r->extended())
|
||||
_part_list[i] = new (&heap)
|
||||
Block::Partition(r->_lba + offset, r->_sectors);
|
||||
});
|
||||
}
|
||||
|
||||
/* Report the partitions */
|
||||
if (reporter.enabled()) {
|
||||
|
||||
enum { PROBE_BYTES = 4096, };
|
||||
Genode::size_t const block_size = driver.blk_size();
|
||||
|
||||
Genode::Reporter::Xml_generator xml(reporter, [&] () {
|
||||
|
||||
if (mbr_valid) {
|
||||
xml.attribute("type", "mbr");
|
||||
|
||||
_parse_mbr(mbr, [&] (int i, Partition_record *r, unsigned offset) {
|
||||
|
||||
Sector fs(driver, r->_lba + offset, PROBE_BYTES / block_size);
|
||||
Fs::Type fs_type = Fs::probe(fs.addr<Genode::uint8_t*>(), PROBE_BYTES);
|
||||
|
||||
xml.node("partition", [&] {
|
||||
xml.attribute("number", i);
|
||||
xml.attribute("type", r->_type);
|
||||
xml.attribute("start", r->_lba + offset);
|
||||
xml.attribute("length", r->_sectors);
|
||||
xml.attribute("block_size", driver.blk_size());
|
||||
|
||||
if (fs_type.valid()) {
|
||||
xml.attribute("file_system", fs_type);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
xml.attribute("type", "disk");
|
||||
|
||||
enum { PART_NUM = 0, };
|
||||
Block::Partition const &disk = *_part_list[PART_NUM];
|
||||
|
||||
Sector fs(driver, disk.lba, PROBE_BYTES / block_size);
|
||||
Fs::Type fs_type = Fs::probe(fs.addr<Genode::uint8_t*>(), PROBE_BYTES);
|
||||
|
||||
xml.node("partition", [&] {
|
||||
xml.attribute("number", PART_NUM);
|
||||
xml.attribute("start", disk.lba);
|
||||
xml.attribute("length", disk.sectors + 1);
|
||||
xml.attribute("block_size", driver.blk_size());
|
||||
|
||||
if (fs_type.valid()) {
|
||||
xml.attribute("file_system", fs_type);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (unsigned num = 0; num < MAX_PARTITIONS; num++)
|
||||
if (_part_list[num])
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _PART_BLOCK__MBR_H_ */
|
86
repos/os/src/server/part_block/partition_table.h
Normal file
86
repos/os/src/server/part_block/partition_table.h
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* \brief Partition table definitions
|
||||
* \author Sebastian Sumpf
|
||||
* \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__PARTITION_TABLE_H_
|
||||
#define _PART_BLOCK__PARTITION_TABLE_H_
|
||||
|
||||
#include <base/env.h>
|
||||
#include <base/log.h>
|
||||
#include <block_session/client.h>
|
||||
#include <os/reporter.h>
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
namespace Block {
|
||||
struct Partition;
|
||||
class Partition_table;
|
||||
}
|
||||
|
||||
|
||||
struct Block::Partition
|
||||
{
|
||||
Genode::uint64_t lba; /* logical block address on device */
|
||||
Genode::uint64_t sectors; /* number of sectors in patitions */
|
||||
|
||||
Partition(Genode::uint64_t l, Genode::uint64_t s)
|
||||
: lba(l), sectors(s) { }
|
||||
};
|
||||
|
||||
|
||||
struct Block::Partition_table : Genode::Interface
|
||||
{
|
||||
class Sector
|
||||
{
|
||||
private:
|
||||
|
||||
Session_client &_session;
|
||||
Packet_descriptor _p;
|
||||
|
||||
public:
|
||||
|
||||
Sector(Driver &driver,
|
||||
unsigned long blk_nr,
|
||||
unsigned long count,
|
||||
bool write = false)
|
||||
: _session(driver.session()),
|
||||
_p(_session.dma_alloc_packet(driver.blk_size() * count),
|
||||
write ? Packet_descriptor::WRITE : Packet_descriptor::READ,
|
||||
blk_nr, count)
|
||||
{
|
||||
_session.tx()->submit_packet(_p);
|
||||
_p = _session.tx()->get_acked_packet();
|
||||
if (!_p.succeeded())
|
||||
Genode::error("Could not access block ",
|
||||
(unsigned long long)_p.block_number());
|
||||
}
|
||||
|
||||
~Sector() { _session.tx()->release_packet(_p); }
|
||||
|
||||
template <typename T> T addr() {
|
||||
return reinterpret_cast<T>(_session.tx()->packet_content(_p)); }
|
||||
};
|
||||
|
||||
Genode::Heap & heap;
|
||||
Driver & driver;
|
||||
Genode::Reporter & reporter;
|
||||
|
||||
Partition_table(Genode::Heap & h, Driver & d, Genode::Reporter & r)
|
||||
: heap(h), driver(d), reporter(r) {}
|
||||
|
||||
virtual Partition *partition(int num) = 0;
|
||||
|
||||
virtual bool parse() = 0;
|
||||
};
|
||||
|
||||
#endif /* _PART_BLOCK__PARTITION_TABLE_H_ */
|
3
repos/os/src/server/part_block/target.mk
Normal file
3
repos/os/src/server/part_block/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = part_block
|
||||
LIBS = base
|
||||
SRC_CC = main.cc
|
Reference in New Issue
Block a user