part_block: modernize implementation

Fixes #4790
This commit is contained in:
Christian Helmuth 2023-03-16 15:50:55 +01:00
parent 082d1780cf
commit 927d71ad59
24 changed files with 906 additions and 468 deletions

View File

@ -0,0 +1 @@
Test part_block server with Atari AHDI disk.

View File

@ -0,0 +1,7 @@
_/src/init
_/src/report_rom
_/src/part_block
_/src/vfs
_/src/vfs_block
_/src/block_tester
_/raw/test-part_block_ahdi

View File

@ -0,0 +1 @@
2023-03-17-d 6690319be80b9b24b647c36303dde6ece6048cb8

View File

@ -0,0 +1,81 @@
<runtime ram="72M" caps="1000" binary="init">
<requires> <timer/> </requires>
<events>
<timeout meaning="failed" sec="70" />
<log meaning="succeeded">
child "test-part*" exited with exit value 0
</log>
<log meaning="failed">Error: </log>
</events>
<content>
<rom label="ld.lib.so"/>
<rom label="vfs_block"/>
<rom label="vfs.lib.so"/>
<rom label="part_block"/>
<rom label="block_tester"/>
<rom label="report_rom"/>
<rom label="ata.ahdi.raw"/>
</content>
<config prio_levels="1" verbose="yes">
<parent-provides>
<service name="ROM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
<service name="Timer"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<default caps="100"/>
<start name="vfs_block">
<resource name="RAM" quantum="32M"/>
<provides><service name="Block"/></provides>
<config>
<vfs>
<rom name="ata.ahdi.raw"/>
</vfs>
<default-policy file="/ata.ahdi.raw" block_size="512"/>
</config>
</start>
<start name="part_block">
<resource name="RAM" quantum="10M" />
<provides><service name="Block" /></provides>
<route>
<any-service><child name="vfs_block"/> <parent/><any-child/></any-service>
</route>
<config>
<report partitions="yes"/>
<policy label_prefix="test-part1" partition="1"/>
</config>
</start>
<start name="report_rom">
<provides>
<service name="Report"/>
<service name="ROM"/>
</provides>
<resource name="RAM" quantum="5M" />
<config verbose="yes"/>
</start>
<start name="test-part1">
<binary name="block_tester"/>
<resource name="RAM" quantum="5M" />
<config verbose="no" log="yes" stop_on_error="no">
<tests>
<sequential length="256K" size="1K" io_buffer="128K" batch="4"/>
</tests>
</config>
<route>
<any-service> <child name="part_block" /> <parent/> <any-child/> </any-service>
</route>
</start>
</config>
</runtime>

View File

@ -0,0 +1 @@
Test part_block server with GPT disk.

View File

@ -0,0 +1,7 @@
_/src/init
_/src/report_rom
_/src/part_block
_/src/vfs
_/src/vfs_block
_/src/block_tester
_/raw/test-part_block_gpt

View File

@ -0,0 +1 @@
2023-03-17 0d6f7581b18066825dd6e9db06faf2c6edb31331

View File

@ -0,0 +1,81 @@
<runtime ram="72M" caps="1000" binary="init">
<requires> <timer/> </requires>
<events>
<timeout meaning="failed" sec="70" />
<log meaning="succeeded">
child "test-part*" exited with exit value 0
</log>
<log meaning="failed">Error: </log>
</events>
<content>
<rom label="ld.lib.so"/>
<rom label="vfs_block"/>
<rom label="vfs.lib.so"/>
<rom label="part_block"/>
<rom label="block_tester"/>
<rom label="report_rom"/>
<rom label="ata.gpt.raw"/>
</content>
<config prio_levels="1" verbose="yes">
<parent-provides>
<service name="ROM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
<service name="Timer"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<default caps="100"/>
<start name="vfs_block">
<resource name="RAM" quantum="32M"/>
<provides><service name="Block"/></provides>
<config>
<vfs>
<rom name="ata.gpt.raw"/>
</vfs>
<default-policy file="/ata.gpt.raw" block_size="512"/>
</config>
</start>
<start name="part_block">
<resource name="RAM" quantum="10M" />
<provides><service name="Block" /></provides>
<route>
<any-service><child name="vfs_block"/> <parent/><any-child/></any-service>
</route>
<config ignore_gpt="true">
<report partitions="yes"/>
<policy label_prefix="test-part0" partition="0"/>
</config>
</start>
<start name="report_rom">
<provides>
<service name="Report"/>
<service name="ROM"/>
</provides>
<resource name="RAM" quantum="5M" />
<config verbose="yes"/>
</start>
<start name="test-part0">
<binary name="block_tester"/>
<resource name="RAM" quantum="5M" />
<config verbose="no" log="yes" stop_on_error="no">
<tests>
<sequential length="1M" size="1K" io_buffer="128K" batch="4"/>
</tests>
</config>
<route>
<any-service> <child name="part_block" /> <parent/> <any-child/> </any-service>
</route>
</start>
</config>
</runtime>

View File

@ -53,7 +53,7 @@
<route>
<any-service><child name="vfs_block"/> <parent/><any-child/></any-service>
</route>
<config use_gpt="yes">
<config>
<report partitions="yes"/>
<policy label_prefix="test-part1" partition="2"/>
<policy label_prefix="test-part2" partition="1"/>

View File

@ -3,7 +3,7 @@
<requires> <timer/> </requires>
<events>
<timeout meaning="failed" sec="60" />
<timeout meaning="failed" sec="70" />
<log meaning="succeeded">
child "test-part*" exited with exit value 0*
child "test-part*" exited with exit value 0*

View File

@ -0,0 +1,4 @@
content: ata.ahdi.raw
ata.ahdi.raw:
tar -xzvf $(REP_DIR)/recipes/raw/test-part_block_ahdi/$@.tar.gz 1>/dev/null

View File

@ -0,0 +1 @@
2023-03-17 9c4b6f3d932b62b0c8d49ebd670aa2cada723988

View File

@ -50,6 +50,10 @@ looks like follows (for MBR resp. GPT).
Clients have read-only access to partitions unless overriden by a 'writeable'
policy attribute.
part_block requests a block session to access the storage device with a
4 MiB I/O buffer per default. The buffer size can be tweaked with the
'io_buffer' config attribute.
Usage
-----
@ -71,7 +75,7 @@ Configuration snippet with two clients and an (hypothetical) IDE driver:
!
! <!-- allow program 'test-part1' to access logical partition '6', while program
! 'test-part2' receives access to primary partition 1, the buffer between
! the 'ata_driver' and 'part_block' is 1 MeB ('io_buffer') -->
! the 'ata_driver' and 'part_block' is 1 MiB ('io_buffer') -->
! <config io_buffer="1M">
! <report partitions="yes"/>
! <policy label_prefix="test-part1" partition="6" writeable="yes"/>

View File

@ -1,6 +1,7 @@
/*
* \brief Atari ST partition scheme (AHDI)
* \author Norman Feske
* \author Christian Helmuth
* \date 2019-08-09
*/
@ -16,84 +17,184 @@
#include "partition_table.h"
namespace Block {
struct Ahdi_partition;
struct Ahdi;
}
struct Ahdi
struct Block::Ahdi_partition : Partition
{
typedef Block::Partition_table::Sector Sector;
using Type = String<4>;
Type type;
typedef Genode::uint32_t uint32_t;
typedef Genode::uint8_t uint8_t;
Ahdi_partition(block_number_t lba,
block_number_t sectors,
Fs::Type fs_type,
Type const &type)
:
Partition(lba, sectors, fs_type),
type(type)
{ }
};
/**
* 32-bit big-endian value
*/
struct Be32
{
uint8_t b0, b1, b2, b3;
uint32_t value() const {
return ((uint32_t)b0 << 24) | ((uint32_t)b1 << 16)
| ((uint32_t)b2 << 8) | ((uint32_t)b3 << 0); }
class Block::Ahdi : public Partition_table
{
private:
} __attribute__((packed));
struct Partition_record
{
uint8_t _flags;
uint8_t _id0, _id1, _id2;
Be32 start; /* first block */
Be32 length; /* in blocks */
typedef Genode::String<4> Id;
Id id() const
/**
* 32-bit big-endian value
*/
struct Be32
{
using Genode::Char;
return Id(Char(_id0), Char(_id1), Char(_id2));
uint8_t b0, b1, b2, b3;
uint32_t value() const {
return ((uint32_t)b0 << 24) | ((uint32_t)b1 << 16)
| ((uint32_t)b2 << 8) | ((uint32_t)b3 << 0); }
} __attribute__((packed));
struct Partition_record
{
uint8_t _flags;
uint8_t _id0, _id1, _id2;
Be32 start; /* first block */
Be32 length; /* in blocks */
Ahdi_partition::Type id() const
{
return { Char(_id0), Char(_id1), Char(_id2) };
}
bool bootable() const { return _flags & 1; }
bool valid() const
{
return start.value() > 0
&& (id() == "BGM" || id() == "GEM" || id() == "LNX");
}
} __attribute__((packed));
enum { MAX_PARTITIONS = 4 };
struct Root_sector
{
uint8_t boot_code[0x156];
Partition_record icd_partitions[8];
uint8_t unused[0xc];
Be32 disk_blocks;
Partition_record partitions[MAX_PARTITIONS];
} __attribute__((packed));
Constructible<Ahdi_partition> _part_list[MAX_PARTITIONS];
bool _valid(Sync_read const &sector)
{
bool any_partition_valid = false;
Root_sector const root = *sector.addr<Root_sector const *>();
for (unsigned i = 0; i < MAX_PARTITIONS; i++)
if (root.partitions[i].valid())
any_partition_valid = true;
return any_partition_valid;
}
bool bootable() const { return _flags & 1; }
template <typename FUNC>
void _parse_ahdi(Sync_read const &sector, FUNC const &fn)
{
Root_sector &root = *sector.addr<Root_sector *>();
bool valid() const { return id() == "BGM" && start.value() > 0; }
for (unsigned i = 0; i < MAX_PARTITIONS; i++) {
Partition_record const &part = root.partitions[i];
if (!part.valid())
continue;
} __attribute__((packed));
enum { MAX_PARTITIONS = 4 };
struct Root_sector
{
uint8_t boot_code[0x156];
Partition_record icd_partitions[8];
uint8_t unused[0xc];
Be32 disk_blocks;
Partition_record partitions[MAX_PARTITIONS];
} __attribute__((packed));
static bool valid(Sector const &sector)
{
bool any_partition_valid = false;
Root_sector const root = *sector.addr<Root_sector const *>();
for (unsigned i = 0; i < MAX_PARTITIONS; i++)
if (root.partitions[i].valid())
any_partition_valid = true;
return any_partition_valid;
}
template <typename FN>
static void for_each_partition(Sector const &sector, FN const &fn)
{
Root_sector &root = *sector.addr<Root_sector *>();
for (unsigned i = 0; i < MAX_PARTITIONS; i++) {
Partition_record const &part = root.partitions[i];
if (part.valid())
fn(i + 1, Block::Partition(part.start.value(), part.length.value(),
Fs::Type(), 0));
fn(i, part);
}
}
template <typename FN>
void _for_each_valid_partition(FN const &fn) const
{
for (unsigned i = 0; i < MAX_PARTITIONS; i++)
if (_part_list[i].constructed())
fn(i);
};
public:
using Partition_table::Partition_table;
bool parse()
{
Sync_read s(_handler, _alloc, 0, 1);
if (!s.success() || !_valid(s))
return false;
_parse_ahdi(s, [&] (unsigned i, Partition_record const &r) {
block_number_t lba = r.start.value();
block_number_t length = r.length.value();
Ahdi_partition::Type type = r.id();
_part_list[i].construct(lba, length, _fs_type(lba), type);
log("AHDI Partition ", i + 1, ": LBA ", lba, " (", length,
" blocks) type: '", type, "'");
});
return true;
}
bool partition_valid(long num) const override
{
/* 1-based partition number to 0-based array index */
num -= 1;
if (num < 0 || num >= MAX_PARTITIONS)
return false;
return _part_list[num].constructed();
}
block_number_t partition_lba(long num) const override
{
return partition_valid(num) ? _part_list[num - 1]->lba : 0;
}
block_number_t partition_sectors(long num) const override
{
return partition_valid(num) ? _part_list[num - 1]->sectors : 0;
}
void generate_report(Xml_generator &xml) const override
{
auto gen_partition_attr = [&] (Xml_generator &xml, unsigned i)
{
Ahdi_partition const &part = *_part_list[i];
xml.attribute("number", i + 1);
xml.attribute("start", part.lba);
xml.attribute("length", part.sectors);
xml.attribute("block_size", _info.block_size);
xml.attribute("type", part.type);
if (part.fs_type.valid())
xml.attribute("file_system", part.fs_type);
};
xml.attribute("type", "ahdi");
_for_each_valid_partition([&] (unsigned i) {
xml.node("partition", [&] {
gen_partition_attr(xml, i); }); });
}
}
};
#endif /* _PART_BLOCK__AHDI_H_ */

View File

@ -0,0 +1,134 @@
/*
* \brief Block I/O utilities
* \author Sebastian Sumpf
* \author Stefan Kalkowski
* \author Christian Helmuth
* \date 2023-03-16
*/
/*
* Copyright (C) 2023 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__BLOCK_H_
#define _PART_BLOCK__BLOCK_H_
#include "types.h"
namespace Block {
struct Job;
struct Sync_read;
}
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 */
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)
{ }
};
/*
* Synchronous block I/O (for reading table info)
*/
class Block::Sync_read : Noncopyable
{
public:
struct Handler : Interface
{
virtual Block_connection & connection() = 0;
virtual void block_for_io() = 0;
};
private:
Handler &_handler;
Allocator &_alloc;
size_t _size { 0 };
void *_buffer { nullptr };
bool _success { false };
/*
* Noncopyable
*/
Sync_read(Sync_read const &) = delete;
Sync_read & operator = (Sync_read const &) = delete;
public:
Sync_read(Handler &handler,
Allocator &alloc,
block_number_t block_number,
block_count_t count)
: _handler(handler), _alloc(alloc)
{
Operation const operation {
.type = Operation::Type::READ,
.block_number = block_number,
.count = count
};
Block_connection::Job job { _handler.connection(), operation };
_handler.connection().update_jobs(*this);
while (!job.completed()) {
_handler.block_for_io();
_handler.connection().update_jobs(*this);
}
}
~Sync_read()
{
_alloc.free(_buffer, _size);
}
bool success() const { return _success; }
void consume_read_result(Block_connection::Job &, off_t offset,
char const *src, size_t length)
{
_buffer = _alloc.alloc(length);
memcpy((char *)_buffer + offset, src, length);
_size += length;
}
void produce_write_content(Block_connection::Job &, off_t,
char *, size_t)
{
}
void completed(Block_connection::Job &, bool success)
{
if (!success)
error("IO error during partition parsing");
_success = success;
}
template <typename T> T addr() const {
return reinterpret_cast<T>(_buffer); }
};
#endif /* _PART_BLOCK__BLOCK_H_ */

View File

@ -0,0 +1,70 @@
/*
* \brief Entire disk a partition 0
* \author Christian Helmuth
* \date 2023-03-17
*/
/*
* Copyright (C) 2023 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__DISK_H_
#define _PART_BLOCK__DISK_H_
#include "partition_table.h"
namespace Block { struct Disk; }
class Block::Disk : public Partition_table
{
private:
Partition _part { 0, _info.block_count, _fs_type(0) };
public:
Disk(Sync_read::Handler &handler,
Allocator &alloc,
Session::Info info)
:
Partition_table(handler, alloc, info)
{
log("DISK Partition 0: LBA ", _part.lba, " (", _part.sectors, " blocks)");
}
bool partition_valid(long num) const override
{
return num == 0;
}
block_number_t partition_lba(long num) const override
{
return partition_valid(num) ? _part.lba : 0;
}
block_number_t partition_sectors(long num) const override
{
return partition_valid(num) ? _part.sectors : 0;
}
void generate_report(Xml_generator &xml) const override
{
xml.attribute("type", "disk");
xml.node("partition", [&] {
xml.attribute("number", 0);
xml.attribute("start", _part.lba);
xml.attribute("length", _part.sectors);
xml.attribute("block_size", _info.block_size);
if (_part.fs_type.valid())
xml.attribute("file_system", _part.fs_type);
});
}
};
#endif /* _PART_BLOCK__DISK_H_ */

View File

@ -1,6 +1,7 @@
/*
* \brief Poor man's partition probe for known file system
* \author Josef Soentgen
* \author Christian Helmuth
* \date 2018-05-03
*/
@ -24,64 +25,68 @@ namespace Fs {
using Type = Genode::String<32>;
Type _probe_extfs(uint8_t *p, size_t len);
Type _probe_fatfs(uint8_t *p, size_t len);
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;
/**
* Probe for Ext2/3/4
*/
Fs::Type Fs::_probe_extfs(uint8_t *p, size_t len)
{
if (len < 4096) { return Type(); }
bool const found_ext_sig = p[0x38] == 0x53 && p[0x39] == 0xEF;
/* super block starts a byte offset 1024 */
p += 0x400;
enum {
COMPAT_HAS_JOURNAL = 0x004u,
INCOMPAT_EXTENTS = 0x040u,
RO_COMPAT_METADATA_CSUM = 0x400u,
};
bool const found_ext_sig = p[0x38] == 0x53 && p[0x39] == 0xEF;
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);
enum {
COMPAT_HAS_JOURNAL = 0x004u,
INCOMPAT_EXTENTS = 0x040u,
RO_COMPAT_METADATA_CSUM = 0x400u,
};
/* the feature flags should denote a given Ext version */
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);
bool const ext3 = compat & COMPAT_HAS_JOURNAL;
bool const ext4 = ext3 && ((incompat & INCOMPAT_EXTENTS)
&& (ro_compat & RO_COMPAT_METADATA_CSUM));
/* the feature flags should denote a given Ext version */
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"); }
bool const ext3 = compat & COMPAT_HAS_JOURNAL;
bool const ext4 = ext3 && ((incompat & INCOMPAT_EXTENTS)
&& (ro_compat & RO_COMPAT_METADATA_CSUM));
return Type();
}
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"); }
/**
* Probe for FAT16/32
*/
Type _probe_fatfs(uint8_t *p, size_t len)
{
if (len < 512) { return Type(); }
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');
bool const gemdos = (p[0] == 0xe9);
/**
* Probe for FAT16/32
*/
Fs::Type Fs::_probe_fatfs(uint8_t *p, size_t len)
{
if (len < 512) { return Type(); }
if (found_boot_sig && fat32) { return Type("FAT32"); }
if (found_boot_sig && fat16) { return Type("FAT16"); }
if (gemdos) { return Type("GEMDOS"); }
/* at least the checks ring true when mkfs.vfat is used... */
bool const found_boot_sig = p[510] == 0x55 && p[511] == 0xAA;
return Type();
}
bool const fat16 = p[38] == 0x28 || p[38] == 0x29;
bool const fat32 = (p[66] == 0x28 || p[66] == 0x29)
&& (p[82] == 'F' && p[83] == 'A');
bool const gemdos = (p[0] == 0xe9);
if (found_boot_sig && fat32) { return Type("FAT32"); }
if (found_boot_sig && fat16) { return Type("FAT16"); }
if (gemdos) { return Type("GEMDOS"); }
return Type();
}

View File

@ -2,6 +2,7 @@
* \brief GUID Partition table definitions
* \author Josef Soentgen
* \author Sebastian Sumpf
* \author Christian Helmuth
* \date 2014-09-19
*/
@ -15,21 +16,38 @@
#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 <util/mmio.h>
#include <util/utf8.h>
#include "partition_table.h"
static bool constexpr verbose = false;
namespace Block {
class Gpt_partition;
class Gpt;
};
struct Block::Gpt_partition : Partition
{
using Uuid = String<40>;
Uuid guid;
Uuid type;
using Name = String<72>; /* use GPT name entry length */
Name name;
Gpt_partition(block_number_t lba,
block_number_t sectors,
Fs::Type fs_type,
Uuid const &guid,
Uuid const &type,
Name const &name)
:
Partition(lba, sectors, fs_type),
guid(guid), type(type), name(name)
{ }
};
class Block::Gpt : public Block::Partition_table
{
private:
@ -37,9 +55,7 @@ class Block::Gpt : public Block::Partition_table
enum { MAX_PARTITIONS = 128 };
/* contains valid partitions or not constructed */
Constructible<Partition> _part_list[MAX_PARTITIONS];
typedef Block::Partition_table::Sector Sector;
Constructible<Gpt_partition> _part_list[MAX_PARTITIONS];
/**
* DCE uuid struct
@ -83,7 +99,6 @@ class Block::Gpt : public Block::Partition_table
* GUID parition table header
*/
struct Gpt_hdr : Mmio
{
struct Sig : Register<0, 64> { }; /* identifies GUID Partition Table */
struct Revision : Register<8, 32> { }; /* GPT specification revision */
@ -147,7 +162,8 @@ class Block::Gpt : public Block::Partition_table
log(" gpe crc: ", Hex(read<Gpe_crc>(), Hex::OMIT_PREFIX));
}
bool valid(Partition_table::Sector_data &data, bool check_primary = true)
bool valid(Sync_read::Handler &handler, Allocator &alloc,
size_t block_size, bool check_primary = true)
{
dump_hdr(check_primary);
@ -172,17 +188,20 @@ class Block::Gpt : public Block::Partition_table
/* check GPT entry array */
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>())
Sync_read gpe(handler, alloc, gpe_lba(), length / block_size);
if (!gpe.success()
|| crc32(gpe.addr<addr_t>(), length) != read<Gpe_crc>())
return false;
if (check_primary) {
/* check backup gpt header */
Sector backup_hdr(data, read<Backup_hdr_lba>(), 1);
Sync_read backup_hdr(handler, alloc, read<Backup_hdr_lba>(), 1);
if (!backup_hdr.success())
return false;
Gpt_hdr backup(backup_hdr.addr<addr_t>());
if (!backup.valid(data, false)) {
if (!backup.valid(handler, alloc, block_size, false))
warning("Backup GPT header is corrupted");
}
}
return true;
@ -336,13 +355,15 @@ class Block::Gpt : public Block::Partition_table
/**
* Parse the GPT header
*/
void _parse_gpt(Gpt_hdr &gpt)
bool _parse_gpt(Gpt_hdr &gpt)
{
if (!(gpt.valid(data)))
throw Exception();
if (!gpt.valid(_handler, _alloc, _info.block_size))
return false;
Sector entry_array(data, gpt.gpe_lba(),
gpt.entries() * gpt.entry_size() / block.info().block_size);
Sync_read entry_array(_handler, _alloc, gpt.gpe_lba(),
gpt.entries() * gpt.entry_size() / _info.block_size);
if (!entry_array.success())
return false;
Gpt_entry entries(entry_array.addr<addr_t>());
_gpt_part_lba_end = gpt.part_lba_end();
@ -356,51 +377,37 @@ class Block::Gpt : public Block::Partition_table
if (!e.valid())
continue;
block_number_t const lba_start = e.lba_start();
block_count_t const length = (block_count_t)(e.lba_end() - lba_start + 1); /* [...) */
enum { BYTES = 4096, };
Sector fs(data, lba_start, BYTES / block.info().block_size);
Fs::Type fs_type = Fs::probe(fs.addr<uint8_t*>(), BYTES);
block_number_t const lba = e.lba_start();
block_number_t const length = e.lba_end() - lba + 1;
String<40> guid { e.guid() };
String<40> type { e.type() };
String<Gpt_entry::NAME_LEN> name { e };
_part_list[i].construct(lba_start, length, fs_type,
guid, type, name);
_part_list[i].construct(lba, length, _fs_type(lba), guid, type, name);
log("GPT Partition ", i + 1, ": LBA ", lba_start, " (", length,
log("GPT Partition ", i + 1, ": LBA ", lba, " (", length,
" blocks) type: '", type,
"' name: '", name, "'");
}
return true;
}
public:
using Partition_table::Partition_table;
Partition &partition(long num) override
bool parse()
{
num -= 1;
Sync_read s(_handler, _alloc, Gpt_hdr::Hdr_lba::LBA, 1);
if (!s.success())
return false;
if (num < 0 || num > MAX_PARTITIONS)
throw -1;
if (!_part_list[num].constructed())
throw -1;
return *_part_list[num];
}
bool parse() override
{
block.sigh(io_sigh);
Sector s(data, Gpt_hdr::Hdr_lba::LBA, 1);
Gpt_hdr gpt_hdr(s.addr<addr_t>());
_parse_gpt(gpt_hdr);
if (!_parse_gpt(gpt_hdr))
return false;
for (unsigned num = 0; num < MAX_PARTITIONS; num++)
if (_part_list[num].constructed())
@ -408,30 +415,49 @@ class Block::Gpt : public Block::Partition_table
return false;
}
bool partition_valid(long num) const override
{
/* 1-based partition number to 0-based array index */
num -= 1;
if (num < 0 || num >= MAX_PARTITIONS)
return false;
return _part_list[num].constructed();
}
block_number_t partition_lba(long num) const override
{
return partition_valid(num) ? _part_list[num - 1]->lba : 0;
}
block_number_t partition_sectors(long num) const override
{
return partition_valid(num) ? _part_list[num - 1]->sectors : 0;
}
void generate_report(Xml_generator &xml) const override
{
xml.attribute("type", "gpt");
uint64_t const total_blocks = block.info().block_count;
uint64_t const total_blocks = _info.block_count;
xml.attribute("total_blocks", total_blocks);
xml.attribute("gpt_total", _gpt_total);
xml.attribute("gpt_used", _gpt_used);
size_t const block_size = block.info().block_size;
_for_each_valid_partition([&] (unsigned i) {
Block::Partition const &part = *_part_list[i];
Gpt_partition const &part = *_part_list[i];
xml.node("partition", [&] () {
xml.attribute("number", i + 1);
xml.attribute("name", part.name);
xml.attribute("type", part.gpt_type);
xml.attribute("type", part.type);
xml.attribute("guid", part.guid);
xml.attribute("start", part.lba);
xml.attribute("length", part.sectors);
xml.attribute("block_size", block_size);
xml.attribute("block_size", _info.block_size);
uint64_t const gap =
_calculate_gap(i, total_blocks);

View File

@ -3,16 +3,18 @@
* \author Sebastian Sumpf
* \author Stefan Kalkowski
* \author Josef Soentgen
* \author Christian Helmuth
* \date 2011-05-30
*/
/*
* Copyright (C) 2011-2020 Genode Labs GmbH
* Copyright (C) 2011-2023 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.
*/
/* Genode includes */
#include <base/attached_rom_dataspace.h>
#include <base/attached_ram_dataspace.h>
#include <base/component.h>
@ -20,10 +22,13 @@
#include <block_session/rpc_object.h>
#include <block/request_stream.h>
#include <os/session_policy.h>
#include <os/reporter.h>
#include <util/bit_allocator.h>
#include "gpt.h"
#include "mbr.h"
#include "ahdi.h"
#include "disk.h"
namespace Block {
class Session_component;
@ -202,7 +207,7 @@ class Block::Session_component : public Rpc_object<Block::Session>,
class Block::Main : Rpc_object<Typed_root<Session>>,
Dispatch
Dispatch, public Sync_read::Handler
{
private:
@ -221,10 +226,15 @@ class Block::Main : Rpc_object<Typed_root<Session>>,
Allocator_avl _block_alloc { &_heap };
Block_connection _block { _env, &_block_alloc, _io_buffer_size };
Session::Info _info { _block.info() };
Io_signal_handler<Main> _io_sigh { _env.ep(), *this, &Main::_handle_io };
Mbr_partition_table _mbr { _env, _block, _heap };
Gpt _gpt { _env, _block, _heap };
Partition_table &_partition_table { _table() };
Mbr _mbr { *this, _heap, _info };
Gpt _gpt { *this, _heap, _info };
Ahdi _ahdi { *this, _heap, _info };
Constructible<Disk> _disk { };
Partition_table & _partition_table { _table() };
enum { MAX_SESSIONS = 128 };
Session_component *_sessions[MAX_SESSIONS] { };
@ -289,6 +299,7 @@ class Block::Main : Rpc_object<Typed_root<Session>>,
Main(Env &env) : _env(env)
{
/* register final handler after initially synchronous block I/O */
_block.sigh(_io_sigh);
/* announce at parent */
@ -326,10 +337,7 @@ class Block::Main : Rpc_object<Typed_root<Session>>,
throw Service_denied();
}
try {
_partition_table.partition(num);
}
catch (...) {
if (!_partition_table.partition_valid(num)) {
error("Partition ", num, " unavailable for '", label, "'");
throw Service_denied();
}
@ -360,7 +368,7 @@ class Block::Main : Rpc_object<Typed_root<Session>>,
Session::Info info {
.block_size = _block.info().block_size,
.block_count = _partition_table.partition(num).sectors,
.block_count = _partition_table.partition_sectors(num),
.align_log2 = 0,
.writeable = writeable,
};
@ -405,7 +413,6 @@ class Block::Main : Rpc_object<Typed_root<Session>>,
void completed(Job &job, bool success)
{
job.request.success = success;
job.completed = true;
}
@ -417,10 +424,9 @@ class Block::Main : Rpc_object<Typed_root<Session>>,
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;
block_number_t last = request.operation.block_number + request.operation.count;
if (last > partition.sectors)
if (last > _partition_table.partition_sectors(number))
return Response::REJECTED;
addr_t index = 0;
@ -431,7 +437,7 @@ class Block::Main : Rpc_object<Typed_root<Session>>,
_job_queue.with_job(index, [&](Job_object &job) {
Operation op = request.operation;
op.block_number += partition.lba;
op.block_number += _partition_table.partition_lba(number);
job.construct(_block, op, _job_registry, index, number, request, addr);
});
@ -457,7 +463,7 @@ class Block::Main : Rpc_object<Typed_root<Session>>,
void acknowledge_completed(bool all = true, long number = -1) override
{
_job_registry.for_each([&] (Job &job) {
if (!job.completed) return;
if (!job.completed()) return;
addr_t index = job.index;
@ -474,6 +480,17 @@ class Block::Main : Rpc_object<Typed_root<Session>>,
_job_queue.free(index);
});
}
/************************
** Sync_read::Handler **
************************/
Block_connection & connection() override { return _block; }
void block_for_io() override
{
_env.ep().wait_and_dispatch_one_io_signal();
}
};
@ -487,6 +504,7 @@ Block::Partition_table & Block::Main::_table()
bool valid_mbr = false;
bool valid_gpt = false;
bool pmbr_found = false;
bool valid_ahdi = false;
bool report = false;
if (ignore_gpt && ignore_mbr) {
@ -505,22 +523,39 @@ Block::Partition_table & Block::Main::_table()
throw;
}
/*
* The initial signal handler can be empty as it's only used to deblock
* wait_and_dispatch_one_io_signal() in Sync_read.
*/
struct Io_dummy { void fn() { }; } io_dummy;
Io_signal_handler<Io_dummy> handler(_env.ep(), io_dummy, &Io_dummy::fn);
_block.sigh(handler);
/*
* 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) {
using Parse_result = Mbr::Parse_result;
switch (_mbr.parse()) {
case Parse_result::MBR:
valid_mbr = true;
break;
case Parse_result::PROTECTIVE_MBR:
pmbr_found = true;
} catch (...) { };
break;
case Parse_result::NO_MBR:
break;
}
}
if (!ignore_gpt) {
try { valid_gpt = _gpt.parse(); }
catch (...) { }
}
if (!ignore_gpt)
valid_gpt = _gpt.parse();
valid_ahdi = _ahdi.parse();
/*
* Both tables are valid (although we would have expected a PMBR in
@ -536,27 +571,31 @@ Block::Partition_table & Block::Main::_table()
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");
}
auto pick_final_table = [&] () -> Partition_table & {
if (valid_gpt) return _gpt;
if (valid_mbr) return _mbr;
if (valid_ahdi) return _ahdi;
/* fall back to entire disk in partition 0 */
_disk.construct(*this, _heap, _info);
return *_disk;
};
Partition_table &table = pick_final_table();
/* generate appropriate report */
if (_reporter.constructed())
if (_reporter.constructed()) {
_reporter->generate([&] (Xml_generator &xml) {
if (valid_gpt) _gpt.generate_report(xml);
if (valid_mbr) _mbr.generate_report(xml);
table.generate_report(xml);
});
}
/*
* 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_partition_table();
return table;
}
void Component::construct(Genode::Env &env) { static Block::Main main(env); }

View File

@ -4,6 +4,7 @@
* \author Stefan Kalkowski
* \author Josef Soentgen
* \author Norman Feske
* \author Christian Helmuth
* \date 2013-12-04
*/
@ -17,29 +18,37 @@
#ifndef _PART_BLOCK__MBR_H_
#define _PART_BLOCK__MBR_H_
#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;
struct Mbr;
};
struct Block::Mbr_partition_table : public Block::Partition_table
struct Block::Mbr_partition : Partition
{
uint8_t const type;
Mbr_partition(block_number_t lba,
block_number_t sectors,
Fs::Type fs_type,
uint8_t type)
:
Partition(lba, sectors, fs_type),
type(type)
{ }
};
class Block::Mbr : public Partition_table
{
public:
class Protective_mbr_found { };
enum class Parse_result { MBR, PROTECTIVE_MBR, NO_MBR };
private:
typedef Block::Partition_table::Sector Sector;
/**
* Partition table entry format
*/
@ -74,16 +83,16 @@ struct Block::Mbr_partition_table : public Block::Partition_table
/**
* Master/Extented boot record format
*/
struct Mbr : Mmio
struct Boot_record : Mmio
{
struct Magic : Register<510, 16>
{
enum { NUMBER = 0xaa55 };
};
Mbr() = delete;
Boot_record() = delete;
Mbr(addr_t base) : Mmio(base) { }
Boot_record(addr_t base) : Mmio(base) { }
bool valid() const
{
@ -97,11 +106,9 @@ struct Block::Mbr_partition_table : public Block::Partition_table
}
};
enum { MAX_PARTITIONS = 32 };
/* contains pointers to valid partitions or 0 */
Constructible<Partition> _part_list[MAX_PARTITIONS];
Constructible<Mbr_partition> _part_list[MAX_PARTITIONS];
template <typename FUNC>
void _parse_extended(Partition_record const &record, FUNC const &f) const
@ -113,9 +120,11 @@ struct Block::Mbr_partition_table : public Block::Partition_table
/* first logical partition number */
int nr = 5;
do {
Sector s(const_cast<Sector_data&>(data), lba, 1);
Mbr const ebr(s.addr<addr_t>());
Sync_read s(_handler, _alloc, lba, 1);
if (!s.success())
return;
Boot_record const ebr(s.addr<addr_t>());
if (!ebr.valid())
return;
@ -140,7 +149,7 @@ struct Block::Mbr_partition_table : public Block::Partition_table
}
template <typename FUNC>
void _parse_mbr(Mbr const &mbr, FUNC const &f) const
Parse_result _parse_mbr(Boot_record const &mbr, FUNC const &f) const
{
for (int i = 0; i < 4; i++) {
Partition_record const r(mbr.record(i));
@ -149,32 +158,15 @@ struct Block::Mbr_partition_table : public Block::Partition_table
continue;
if (r.protective())
throw Protective_mbr_found();
return Parse_result::PROTECTIVE_MBR;
f(i + 1, r, 0);
if (r.extended())
_parse_extended(r, f);
}
}
/* state for partitions report */
bool _mbr_valid { false };
bool _ahdi_valid { false };
public:
using Partition_table::Partition_table;
Partition &partition(long num) override
{
if (num < 0 || num > MAX_PARTITIONS)
throw -1;
if (!_part_list[num].constructed())
throw -1;
return *_part_list[num];
return Parse_result::MBR;
}
template <typename FN>
@ -185,96 +177,78 @@ struct Block::Mbr_partition_table : public Block::Partition_table
fn(i);
};
bool parse() override
{
block.sigh(io_sigh);
public:
Sector s(data, 0, 1);
using Partition_table::Partition_table;
Parse_result parse()
{
Sync_read s(_handler, _alloc, 0, 1);
if (!s.success())
return Parse_result::NO_MBR;
/* check for MBR */
Mbr const mbr(s.addr<addr_t>());
_mbr_valid = mbr.valid();
if (_mbr_valid) {
_parse_mbr(mbr, [&] (int i, Partition_record const &r, unsigned offset) {
log("MBR Partition ", i, ": LBA ",
r.lba() + offset, " (",
r.sectors(), " blocks) type: ",
Hex(r.type(), Hex::OMIT_PREFIX));
Boot_record const mbr(s.addr<addr_t>());
if (!mbr.valid())
return Parse_result::NO_MBR;
if (!r.extended()) {
return _parse_mbr(mbr, [&] (int nr, Partition_record const &r, unsigned offset)
{
if (!r.extended()) {
block_number_t const lba = r.lba() + offset;
block_number_t const lba = r.lba() + offset;
_part_list[nr - 1].construct(lba, r.sectors(), _fs_type(lba), r.type());
}
/* probe for known file-system types */
enum { PROBE_BYTES = 4096, };
Sector fs(data, lba , PROBE_BYTES / block.info().block_size);
Fs::Type const fs_type =
Fs::probe(fs.addr<uint8_t*>(), PROBE_BYTES);
log("MBR Partition ", nr, ": LBA ",
r.lba() + offset, " (",
r.sectors(), " blocks) type: ",
Hex(r.type(), Hex::OMIT_PREFIX));
});
}
_part_list[i].construct(
Partition(lba, r.sectors(), fs_type, r.type()));
}
});
}
bool partition_valid(long num) const override
{
/* 1-based partition number to 0-based array index */
num -= 1;
/* check for AHDI partition table */
_ahdi_valid = !_mbr_valid && Ahdi::valid(s);
if (_ahdi_valid)
Ahdi::for_each_partition(s, [&] (unsigned i, Partition info) {
if (i < MAX_PARTITIONS)
_part_list[i].construct(
Partition(info.lba, info.sectors, Fs::Type(), 0));
});
if (num < 0 || num >= MAX_PARTITIONS)
return false;
/* no partition table, use whole disc as partition 0 */
if (!_mbr_valid && !_ahdi_valid)
_part_list[0].construct(
Partition(0, (block_count_t)(block.info().block_count - 1),
Fs::Type(), 0));
return _part_list[num].constructed();
}
bool any_partition_valid = false;
_for_each_valid_partition([&] (unsigned) {
any_partition_valid = true; });
block_number_t partition_lba(long num) const override
{
return partition_valid(num) ? _part_list[num - 1]->lba : 0;
}
return any_partition_valid;
block_number_t partition_sectors(long num) const override
{
return partition_valid(num) ? _part_list[num - 1]->sectors : 0;
}
void generate_report(Xml_generator &xml) const override
{
auto gen_partition_attr = [&] (Xml_generator &xml, unsigned i)
{
Partition const &part = *_part_list[i];
Mbr_partition const &part = *_part_list[i];
size_t const block_size = block.info().block_size;
xml.attribute("number", i);
xml.attribute("number", i + 1);
xml.attribute("start", part.lba);
xml.attribute("length", part.sectors);
xml.attribute("block_size", block_size);
if (_mbr_valid)
xml.attribute("type", part.mbr_type);
else if (_ahdi_valid)
xml.attribute("type", "bgm");
xml.attribute("block_size", _info.block_size);
xml.attribute("type", part.type);
if (part.fs_type.valid())
xml.attribute("file_system", part.fs_type);
};
xml.attribute("type", _mbr_valid ? "mbr" :
_ahdi_valid ? "ahdi" :
"disk");
if (_mbr_valid || _ahdi_valid) {
_for_each_valid_partition([&] (unsigned i) {
xml.node("partition", [&] {
gen_partition_attr(xml, i); }); });
} else {
xml.attribute("type", "mbr");
_for_each_valid_partition([&] (unsigned i) {
xml.node("partition", [&] {
gen_partition_attr(xml, 0); });
}
gen_partition_attr(xml, i); }); });
}
};

View File

@ -2,6 +2,7 @@
* \brief Partition table definitions
* \author Sebastian Sumpf
* \author Stefan Kalkowski
* \author Christian Helmuth
* \date 2013-12-04
*/
@ -15,193 +16,59 @@
#ifndef _PART_BLOCK__PARTITION_TABLE_H_
#define _PART_BLOCK__PARTITION_TABLE_H_
#include <base/env.h>
#include <base/log.h>
#include <base/registry.h>
#include <block_session/connection.h>
#include <os/reporter.h>
#include "block.h"
#include "fsprobe.h"
#include "types.h"
namespace Block {
struct Partition;
class Partition_table;
struct Job;
using namespace Genode;
typedef Block::Connection<Job> Block_connection;
}
struct Block::Partition
struct Block::Partition : Noncopyable
{
block_number_t lba; /* logical block address on device */
block_count_t sectors; /* number of sectors in patitions */
block_number_t const lba; /* logical block address on device */
block_number_t const sectors; /* number of sectors in partitions */
Fs::Type fs_type { };
uint8_t mbr_type { 0 };
using Uuid = String<40>;
Uuid guid { };
Uuid gpt_type { };
using Name = String<72>; /* use GPT name antry length */
Name name { };
Partition(block_number_t lba,
block_count_t sectors,
Fs::Type fs_type,
uint8_t mbr_type)
:
lba(lba), sectors(sectors), fs_type(fs_type),
mbr_type(mbr_type)
{ }
Partition(block_number_t lba,
block_count_t sectors,
Fs::Type fs_type,
Uuid const &guid, Uuid const &gpt_type,
Name const &name)
:
lba(lba), sectors(sectors), fs_type(fs_type),
guid(guid), gpt_type(gpt_type), name(name)
{ }
block_number_t sectors,
Fs::Type fs_type)
: lba(lba), sectors(sectors), fs_type(fs_type) { }
};
struct Block::Job : public Block_connection::Job
class Block::Partition_table : Interface, Noncopyable
{
Registry<Job>::Element registry_element;
protected:
addr_t const index; /* job index */
long const number; /* parition number */
Request request;
addr_t const addr; /* target payload address */
bool completed { false };
Sync_read::Handler &_handler;
Allocator &_alloc;
Session::Info const _info;
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
Fs::Type _fs_type(block_number_t lba)
{
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:
Sector_data &_data;
bool _completed { false };
size_t _size { 0 };
void *_buffer { nullptr };
Sector(Sector const &);
Sector &operator = (Sector const &);
public:
Sector(Sector_data &data,
block_number_t block_number,
block_count_t count)
: _data(data)
{
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()
{
_data.alloc.free(_buffer, _size);
}
void handle_io()
{
_data.block.update_jobs(*this);
}
void consume_read_result(Block_connection::Job &, off_t offset,
char const *src, size_t length)
{
_buffer = _data.alloc.alloc(length);
memcpy((char *)_buffer + offset, 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>(_buffer); }
};
Env &env;
Block_connection &block;
Sector_data data;
Io_signal_handler<Partition_table> io_sigh {
env.ep(), *this, &Partition_table::handle_io };
void handle_io()
{
if (data.current) { data.current->handle_io(); }
/* probe for known file-system types */
enum { BYTES = 4096 };
Sync_read fs(_handler, _alloc, lba, BYTES / _info.block_size);
if (fs.success())
return Fs::probe(fs.addr<uint8_t*>(), BYTES);
else
return Fs::Type();
}
Partition_table(Env &env,
Block_connection &block,
Allocator &alloc)
: env(env), block(block), data(env, block, alloc)
{ }
public:
virtual Partition &partition(long num) = 0;
Partition_table(Sync_read::Handler &handler,
Allocator &alloc,
Session::Info info)
: _handler(handler), _alloc(alloc), _info(info) { }
virtual bool parse() = 0;
virtual bool partition_valid(long num) const = 0;
virtual block_number_t partition_lba(long num) const = 0;
virtual block_number_t partition_sectors(long num) const = 0;
virtual void generate_report(Xml_generator &xml) const = 0;
};

View File

@ -0,0 +1,33 @@
/*
* \brief Types used by part_block
* \author Christian Helmuth
* \date 2023-03-16
*/
/*
* Copyright (C) 2023 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__TYPES_H_
#define _PART_BLOCK__TYPES_H_
/* Genode includes */
#include <base/registry.h>
#include <base/log.h>
#include <util/xml_generator.h>
#include <util/mmio.h>
#include <util/misc_math.h>
#include <util/utf8.h>
#include <block_session/connection.h>
namespace Block {
using namespace Genode;
struct Job;
typedef Block::Connection<Job> Block_connection;
}
#endif /* _PART_BLOCK__TYPES_H_ */