diff --git a/repos/gems/run/gpt_write.run b/repos/gems/run/gpt_write.run
new file mode 100644
index 0000000000..1ace87a057
--- /dev/null
+++ b/repos/gems/run/gpt_write.run
@@ -0,0 +1,73 @@
+#
+# Test runs only on Linux
+#
+assert_spec linux
+
+#
+# Build
+#
+build { core init drivers/timer server/lx_block app/gpt_write }
+
+create_boot_directory
+
+#
+# Generate config
+#
+install_config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+#
+# Create test file
+#
+catch { exec dd if=/dev/zero of=bin/gpt.img bs=1M count=256 }
+
+#
+# Boot modules
+#
+build_boot_image {
+ core ld.lib.so init timer lx_block gpt_write
+ gpt.img
+}
+
+run_genode_until {child "gpt_write" exited with exit value 0.*\n} 10
+
+exec rm -f bin/gpt.img
diff --git a/repos/gems/src/app/gpt_write/README b/repos/gems/src/app/gpt_write/README
new file mode 100644
index 0000000000..c4dcdb3c93
--- /dev/null
+++ b/repos/gems/src/app/gpt_write/README
@@ -0,0 +1,99 @@
+Brief
+=====
+
+This component creates a GPT on a Block device. It supports the common
+actions, as in adding, deleting and modifying entries in the GPT, while
+considering alignment constraints. If needed it will round the length of
+a partition down to meet those constraints. The component will not perform
+layout checking, i.e., it does not care about overlapping partitions. Only
+when apping a partition it will make sure that the partition will fit.
+
+As a temporary limitation it does not handle Block devices with a logical
+block size of 4096 or larger well.
+
+
+Configuration
+=============
+
+The component is instructed to manage the GPT by specifying actions.
+The actions are:
+
+ * 'add' for adding a new partition. The used entry is set by
+ the 'entry' attribute, while the 'start' attributes denotes the
+ starting LBA of the partition. If the 'entry' attribute is missing,
+ the component operates in append-mode and the next free entry will
+ be used, while the component selects a proper start LBA and ignores
+ the 'start' attribute. The mandatory 'label', 'size' and 'type'
+ attributes must be used to set the corresponding property.
+
+ * 'delete' for removing partitions by clearing the corresponding entry.
+ The entry can by selected by using either the 'entry' or 'label'
+ attribute.
+
+ * 'modify' for changing properties of an existing entry. The
+ entry can be selected by using either the 'entry' or 'label'
+ attribute. The 'new_label', 'new_size' and 'new_type' attributes
+ may be set to change the corresponding property.
+
+The size-related attributes, 'size' and 'new_size', can take as value
+a suffixed number (K, M, G) to interpret the value to the base of 2^10,
+2^20 or 2^30. As special treatment it is also possible to use 'max' as
+value. In this case the component will try to use all free space between
+this partition and the next partition or, for that matter, the end of the
+addressable space within the GPT.
+
+The type-related attributes, 'type' and 'new_type', take a identifier,
+which selects the proper GUID. Valid identifiers are:
+
+ * 'EFI' for the EFI system partition type
+ * 'BIOS' for a BIOS boot partition type (for GRUB to store its core.img)
+ * 'BDP' for basic data partition type (Windows/DOS file systems)
+ * 'Linux' for a Linux file system data type (Linux file systems)
+
+The label-related attributes, 'label' and 'new_label', take a ASCII string,
+i.e., [A-Za-z- *].
+
+There are config node attributes, in addition to the actions, that instruct
+the component to perform additional tasks.
+
+To remove the GPT headers as well as the protective MBR the 'wipe'
+attribute can be specified:
+
+!
+
+In this case 'gpt_write' will only perform the wiping and than exit,
+even if other attributes are set or actions specified.
+
+To update the GPT information, in case the underlying Block device has
+changed (for example a generated disk image is copied to a larger USB
+stick), the 'update_geometry' attribute may be used to increase the
+useable space of the GPT:
+
+!
+
+Setting the 'initialize' attribute will instruct the component to clear
+any existing GPT/PMBR.
+
+!
+
+The alignment of partitions can by specified by setting the 'align'
+attribute. It defaults to '4096' if not set.
+
+These attributes can be freely mixed, considering the constraint of
+using 'wipe'.
+
+The following snippet shows a examplary configuration:
+
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+
diff --git a/repos/gems/src/app/gpt_write/gpt.h b/repos/gems/src/app/gpt_write/gpt.h
new file mode 100644
index 0000000000..e06c083f9a
--- /dev/null
+++ b/repos/gems/src/app/gpt_write/gpt.h
@@ -0,0 +1,453 @@
+/*
+ * \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 _GPT_H_
+#define _GPT_H_
+
+/* Genode includes */
+#include
+
+/* local includes */
+#include
+
+namespace Gpt {
+
+ using namespace Genode;
+
+ /***************
+ ** Datatypes **
+ ***************/
+
+ using Type = Genode::String<32>;
+ using Label = Genode::String<32>;
+
+ /**
+ * DCE uuid struct
+ */
+ struct Uuid
+ {
+ enum { UUID_NODE_LEN = 6 };
+
+ uint32_t time_low;
+ uint16_t time_mid;
+ uint16_t time_hi_and_version;
+ uint8_t clock_seq_hi_and_reserved;
+ uint8_t clock_seq_low;
+ uint8_t node[UUID_NODE_LEN];
+
+ bool valid() const {
+ return time_low != 0 && time_hi_and_version != 0;
+ }
+ } __attribute__((packed));
+
+
+ /**
+ * GUID parition table header
+ */
+ struct Header
+ {
+ char signature[8]; /* identifies GUID Partition Table */
+ uint32_t revision; /* GPT specification revision */
+ uint32_t size; /* size of GPT header */
+ uint32_t crc; /* CRC32 of GPT header */
+ uint32_t reserved; /* must be zero */
+ uint64_t lba; /* LBA that contains this header */
+ uint64_t backup_lba; /* LBA of backup GPT header */
+ uint64_t part_lba_start; /* first LBA usable for partitions */
+ uint64_t part_lba_end; /* last LBA usable for partitions */
+ Uuid guid; /* GUID to identify the disk */
+ uint64_t gpe_lba; /* first LBA of GPE array */
+ uint32_t gpe_num; /* number of entries in GPE array */
+ uint32_t gpe_size; /* size of each GPE */
+ uint32_t gpe_crc; /* CRC32 of GPE array */
+
+ /* the remainder of the struct must be zero */
+
+ bool valid(bool primary = true)
+ {
+ /* check sig */
+ if (strcmp(signature, "EFI PART", 8) != 0) {
+ return false;
+ }
+
+ /* check header crc */
+ uint32_t const save_crc = crc;
+ crc = 0;
+ if (Util::crc32(this, size) != save_crc) {
+ error("wrong header checksum");
+ return false;
+ }
+
+ crc = save_crc;
+
+ /* check header lba */
+ if ((primary ? lba : backup_lba) != 1) {
+ return false;
+ }
+
+ return true;
+ }
+
+ bool entries_valid(void const *entries) const
+ {
+ size_t const length = gpe_num * gpe_size;
+ return Util::crc32(entries, length) == gpe_crc ? true : false;
+ }
+ } __attribute__((packed));
+
+
+ /**
+ * GUID partition entry format
+ */
+ struct Entry
+ {
+ enum { NAME_LEN = 36 };
+ Uuid type; /* partition type GUID */
+ Uuid guid; /* unique partition GUID */
+ uint64_t lba_start; /* start of partition */
+ uint64_t lba_end; /* end of partition */
+ uint64_t attributes; /* partition attributes */
+ uint16_t name[NAME_LEN]; /* partition name in UTF-16LE */
+
+ bool valid() const { return type.valid(); }
+
+ /**
+ * Extract all valid ASCII characters in the name entry
+ */
+ bool read_name(char *dest, size_t dest_len) const
+ {
+ return !!Util::extract_ascii(dest, dest_len, name, NAME_LEN);
+ }
+
+ /**
+ * Write ASCII to name field
+ */
+ bool write_name(Label const &label)
+ {
+ return !!Util::convert_ascii(name, NAME_LEN,
+ (uint8_t*)label.string(),
+ label.length() - 1);
+ }
+
+ uint64_t length() const { return lba_end - lba_start + 1; }
+ } __attribute__((packed));
+
+
+ /*****************
+ ** Definitions **
+ *****************/
+
+ enum { REVISION = 0x00010000u, };
+
+ enum {
+ MIN_ENTRIES = 128u,
+ MAX_ENTRIES = MIN_ENTRIES,
+ ENTRIES_SIZE = sizeof(Entry) * MAX_ENTRIES,
+ PGPT_LBA = 1,
+ };
+
+
+ /***************
+ ** Functions **
+ ***************/
+
+ /**
+ * Convert type string to UUID
+ *
+ * \param type type string
+ *
+ * \return UUID in case the type is known, otherwise exception is thrown
+ *
+ * \throw Invalid_type
+ */
+ Uuid type_to_uuid(Type const &type)
+ {
+ struct {
+ char const *type;
+ Uuid uuid;
+ } _gpt_types[] = {
+ /* EFI System Partition */
+ { "EFI", 0xC12A7328, 0xF81F, 0x11D2, 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B },
+ /* BIOS Boot Partition (GRUB) */
+ { "BIOS", 0x21686148, 0x6449, 0x6E6F, 0x74, 0x4E, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49 },
+ /* Basic Data Partition (FAT32, exFAT, NTFS, ...) */
+ { "BDP", 0xEBD0A0A2, 0xB9E5, 0x4433, 0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7 },
+ /* Linux Filesystem Data (for now used by Genode for Ext2 rootfs) */
+ { "Linux", 0x0FC63DAF, 0x8483, 0x4772, 0x8E, 0x79, 0x3D, 0x69, 0xD8, 0x47, 0x7D, 0xE4 },
+ };
+
+ size_t const num = sizeof(_gpt_types) / sizeof(_gpt_types[0]);
+ for (size_t i = 0; i < num; i++) {
+ if (type == _gpt_types[i].type) {
+ return _gpt_types[i].uuid;
+ }
+ }
+
+ struct Invalid_type : Genode::Exception { };
+ throw Invalid_type();
+ }
+
+
+ /**
+ * Generate random UUID (RFC 4122 4.4)
+ *
+ * \return random UUID
+ */
+ Uuid generate_uuid()
+ {
+ uint8_t buf[sizeof(Uuid)];
+ Util::get_random(buf, sizeof(buf));
+
+ Uuid uuid;
+ Genode::memcpy(&uuid, buf, sizeof(buf));
+
+ uuid.time_hi_and_version =
+ (uuid.time_hi_and_version & 0x0fff) | 0x4000;
+ uuid.clock_seq_hi_and_reserved =
+ (uuid.clock_seq_hi_and_reserved & 0x3f) | 0x80;
+
+ return uuid;
+ }
+
+
+ /**
+ * Get block gap to next logical entry
+ *
+ * \param header pointer to GPT header
+ * \param entry pointer to current entry
+ * \param entries pointer to entries
+ *
+ * \return the number of free blocks to the next logical entry
+ */
+ Genode::uint64_t gap_length(Gpt::Header const &header,
+ Gpt::Entry const *entries,
+ Gpt::Entry const *entry)
+ {
+ using namespace Genode;
+
+ /* add one block => end == start */
+ uint64_t const end_lba = entry ? entry->lba_end + 1 : ~0ull;
+
+ enum { INVALID_START = ~0ull, };
+ uint64_t next_start_lba = INVALID_START;
+
+ for (uint32_t i = 0; i < header.gpe_num; i++) {
+ 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);
+ }
+ }
+
+ /*
+ * 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 ? header.part_lba_end
+ : next_start_lba) - end_lba;
+ }
+
+
+ /**
+ * Find free GPT entry
+ *
+ * \param header reference to GPT header
+ * \param entries pointer to memory containing GPT entries
+ *
+ * \return if a free entry is found a pointer to its memory
+ * is returned, otherwise a null pointer is returned
+ */
+ Entry *find_free(Header const &header, Entry *entries)
+ {
+ if (!entries) { return nullptr; }
+
+ Entry *result = nullptr;
+ for (size_t i = 0; i < header.gpe_num; i++) {
+ Entry *e = (entries + i);
+
+ if (e->valid()) { continue; }
+
+ result = e;
+ break;
+ }
+
+ return result;
+ }
+
+
+ /**
+ * Get last valid entry
+ *
+ * \param header reference to GPT header
+ * \param entries pointer to memory containing GPT entries
+ *
+ *
+ * \return if a free entry is found a pointer to its memory
+ * is returned, otherwise a null pointer is returned
+ */
+ Entry const *find_last_valid(Header const &header, Entry const *entries)
+ {
+ if (!entries) { return nullptr; }
+
+ Entry const *result = nullptr;
+ for (size_t i = 0; i < header.gpe_num; i++) {
+ Entry const * const e = (entries + i);
+
+ if (e->valid()) { result = e; }
+ }
+
+ return result;
+ }
+
+
+ /**
+ * Get next free entry
+ */
+ Entry *find_next_free(Header const &header, Entry *entries,
+ Entry const *entry)
+ {
+ if (!entries || !entry) { return nullptr; }
+
+ size_t const num = (entry - entries + 1);
+ if (num >= header.gpe_num) { return nullptr; }
+
+ Entry *result = nullptr;
+ for (size_t i = num; i < header.gpe_num; i++) {
+ Entry * const e = (entries + i);
+
+ if (e->valid()) { continue; }
+
+ result = e;
+ break;
+ }
+
+ return result;
+ }
+
+
+ /**
+ * Lookup GPT entry
+ *
+ * \param entries pointer to memory containing GPT entries
+ * \param num number of GPT entries
+ * \param label name of the partition in the entry
+ *
+ * \return if entry is found a pointer to its memory location
+ * is returned, otherwise a null pointer is returned
+ */
+ Entry *lookup_entry(Entry *entries, size_t num, Label const &label)
+ {
+ if (!entries) { return nullptr; }
+
+ Entry *result = nullptr;
+
+ char tmp[48];
+
+ for (size_t i = 0; i < num; i++) {
+ Entry *e = (entries + i);
+
+ if (!e->valid()) { continue; }
+
+ if (!e->read_name(tmp, sizeof(tmp))) { continue; }
+
+ if (Genode::strcmp(label.string(), tmp) == 0) {
+ result = e;
+ break;
+ }
+ }
+
+ return result;
+ }
+
+
+ /**
+ * Get number of entry
+ *
+ * \param entries pointer to memory containing GPT entries
+ * \param entry pointer to memory containing GPT entry
+ *
+ * \return number of entry
+ */
+ uint32_t entry_num(Entry const *entries, Entry const *e)
+ {
+ return e - entries + 1;
+ }
+
+
+ /**
+ * Get used blocks
+ *
+ * \param header reference to GPT header
+ * \param entries pointer to memory containing GPT entries
+ *
+ * \return if a free entry is found a pointer to its memory
+ * is returned, otherwise a null pointer if returned
+ */
+ uint64_t get_used_blocks(Header const &header, Entry const *entries)
+ {
+ if (!entries) { return ~0ull; }
+
+ uint64_t result = 0;
+
+ for (size_t i = 0; i < header.gpe_num; i++) {
+ Entry const * const e = (entries + i);
+
+ if (!e->valid()) { continue; }
+
+ uint64_t const blocks = e->lba_end - e->lba_start;
+ result += blocks;
+ }
+
+ return result;
+ }
+
+
+ /**
+ * Check if given GPT header and entries are valid
+ *
+ * \param header reference to header
+ * \param entries pointer to memory containing GPT entries
+ * \param primary if true, header is assumed to be the primary
+ * and otherwise to be the backup header
+ *
+ * \return true if header and entries are valid, otherwise false
+ */
+ bool valid(Header &header, Entry const *e, bool primary)
+ {
+ return header.valid(primary)
+ && header.entries_valid(e) ? true : false;
+ }
+
+ /**
+ * Update CRC32 fields
+ *
+ * \param header reference to header
+ * \param entries pointer to memory containing GPT entries
+ */
+ void update_crc32(Header &header, void const *entries)
+ {
+ size_t const len = header.gpe_size * header.gpe_num;
+ header.gpe_crc = Util::crc32(entries, len);
+ header.crc = 0;
+ header.crc = Util::crc32(&header, header.size);
+ }
+
+} /* namespace Gpt */
+
+#endif /* _GPT_H_ */
diff --git a/repos/gems/src/app/gpt_write/main.cc b/repos/gems/src/app/gpt_write/main.cc
new file mode 100644
index 0000000000..0f853e75ff
--- /dev/null
+++ b/repos/gems/src/app/gpt_write/main.cc
@@ -0,0 +1,765 @@
+/*
+ * \brief GPT partitioning tool
+ * \author Josef Soentgen
+ * \date 2018-05-01
+ */
+
+/*
+ * 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.
+ */
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+
+/* local includes */
+#include
+#include
+
+
+namespace Gpt {
+
+ struct Writer;
+}
+
+struct Gpt::Writer
+{
+ struct Io_error : Genode::Exception { };
+ struct Gpt_invalid : Genode::Exception { };
+
+ using sector_t = Block::sector_t;
+
+ Block::Connection &_block;
+ Block::Session::Operations _block_ops { };
+ Block::sector_t _block_count { 0 };
+ size_t _block_size { 0 };
+
+ /*
+ * Blocks available is a crude approximation that _does not_ take
+ * alignment or unusable blocks because of the layout into account!
+ */
+ uint64_t _blocks_avail { 0 };
+
+ /* track actions */
+ bool _new_gpt { false };
+ bool _new_pmbr { false };
+ bool _new_geometry { false };
+
+ /* flags */
+ bool _verbose { false };
+ bool _update_geometry { false };
+ bool _initialize { false };
+ bool _wipe { false };
+ bool _force_alignment { false };
+
+ Util::Number_of_bytes _entry_alignment { 4096u };
+
+ Genode::Xml_node *_config { nullptr };
+
+ void _handle_config(Genode::Xml_node config)
+ {
+ _verbose = config.attribute_value("verbose", false);
+ _initialize = config.attribute_value("initialize", false);
+ _wipe = config.attribute_value("wipe", false);
+ _force_alignment = config.attribute_value("force_align", false);
+ _update_geometry = config.attribute_value("update_geometry", false);
+
+ {
+ Util::Size_string align =
+ config.attribute_value("align", Util::Size_string(4096u));
+ ascii_to(align.string(), _entry_alignment);
+ }
+
+ bool const commands = config.has_sub_node("commands");
+
+ if (_wipe && (_initialize || commands)) {
+ Genode::warning("will exit after wiping");
+ }
+
+ _config = &config;
+ }
+
+ /************
+ ** Tables **
+ ************/
+
+ Protective_mbr::Header _pmbr { };
+
+ Gpt::Header _pgpt { };
+ Gpt::Entry _pgpt_entries[MAX_ENTRIES] { };
+
+ Gpt::Header _bgpt { };
+ Gpt::Entry _bgpt_entries[MAX_ENTRIES] { };
+
+ Block::sector_t _old_backup_hdr_lba { 0 };
+
+ /**
+ * Fill in-memory GPT header/entries and valid
+ *
+ * \param primary if set to true the primary GPT is checked, if false
+ * the backup header is checked
+ *
+ * \throw Io_error
+ */
+ void _fill_and_check_header(bool primary)
+ {
+ using namespace Genode;
+
+ Gpt::Header *hdr = primary ? &_pgpt : &_bgpt;
+ Gpt::Entry *entries = primary ? _pgpt_entries : _bgpt_entries;
+
+ memset(hdr, 0, sizeof(Gpt::Header));
+ memset(entries, 0, ENTRIES_SIZE);
+
+ try {
+ /* header */
+ {
+ Block::sector_t const lba = primary
+ ? 1 : _pgpt.backup_lba;
+ Util::Block_io io(_block, _block_size, lba, 1);
+ memcpy(hdr, io.addr(), sizeof(Gpt::Header));
+
+ if (!hdr->valid(primary)) {
+ error(primary ? "primary" : "backup",
+ " GPT header not valid");
+ throw Gpt_invalid();
+ }
+ }
+
+ /* entries */
+ {
+ uint32_t const max_entries = hdr->gpe_num > (uint32_t)MAX_ENTRIES
+ ? (uint32_t)MAX_ENTRIES : hdr->gpe_num;
+ Block::sector_t const lba = hdr->gpe_lba;
+ size_t const count = max_entries * hdr->gpe_size / _block_size;
+
+ Util::Block_io io(_block, _block_size, lba, count);
+ size_t const len = count * _block_size;
+ memcpy(entries, io.addr(), len);
+ }
+ } catch (Util::Block_io::Io_error) {
+ error("could not read GPT header/entries");
+ throw Io_error();
+ }
+
+ if (!Gpt::valid(*hdr, entries, primary)) {
+ error("GPT header and entries not valid");
+ throw Gpt_invalid();
+ }
+ }
+
+ /**
+ * Wipe old backup GPT header from Block device
+ */
+ bool _wipe_old_backup_header()
+ {
+ enum { BLOCK_SIZE = 4096u, };
+ if (_block_size > BLOCK_SIZE) {
+ Genode::error("block size of ", _block_size, "not supported");
+ return false;
+ }
+
+ char zeros[BLOCK_SIZE] { };
+
+ size_t const blocks = 1 + (ENTRIES_SIZE / _block_size);
+ Block::sector_t const lba = _old_backup_hdr_lba - blocks;
+
+ using namespace Util;
+
+ try {
+ Block_io clear(_block, _block_size, lba, blocks, true, zeros, _block_size);
+ } catch (Block_io::Io_error) { return false; }
+
+ return true;
+ }
+
+ /**
+ * Wipe all tables from Block device
+ *
+ * Note: calling this method actually destroys old data!
+ */
+ bool _wipe_tables()
+ {
+ enum { BLOCK_SIZE = 4096u, };
+ if (_block_size > BLOCK_SIZE) {
+ Genode::error("block size of ", _block_size, "not supported");
+ return false;
+ }
+
+ char zeros[BLOCK_SIZE] { };
+
+ using namespace Util;
+
+ try {
+ /* PMBR */
+ Block_io clear_mbr(_block, _block_size, 0, 1, true, zeros, _block_size);
+ size_t const blocks = 1 + (Gpt::ENTRIES_SIZE / _block_size);
+
+ /* PGPT */
+ for (size_t i = 0; i < blocks; i++) {
+ Block_io clear_lba(_block, _block_size, 1 + i, 1,
+ true, zeros, _block_size);
+ }
+
+ /* BGPT */
+ for (size_t i = 0; i < blocks; i++) {
+ Block_io clear_lba(_block, _block_size, (_block_count - 1) - i, 1,
+ true, zeros, _block_size);
+ }
+
+ return true;
+ } catch (Block_io::Io_error) { return false; }
+ }
+
+ /**
+ * Setup protective MBR
+ *
+ * The first protective partition covers the whole Block device from the
+ * second block up to the 32bit boundary.
+ */
+ void _setup_pmbr()
+ {
+ _pmbr.partitions[0].type = Protective_mbr::TYPE_PROTECTIVE;
+ _pmbr.partitions[0].lba = 1;
+ _pmbr.partitions[0].sectors = (uint32_t)(_block_count - 1);
+
+ _new_pmbr = true;
+ }
+
+ /**
+ * Initialize tables
+ *
+ * All tables, primary GPT and backup GPT as well as the protective MBR
+ * will be cleared in memory, a new disk GUID will be generated and the
+ * default values will be set.
+ */
+ void _initialize_tables()
+ {
+ _setup_pmbr();
+
+ /* wipe PGPT and BGPT */
+ Genode::memset(&_pgpt, 0, sizeof(_pgpt));
+ Genode::memset(_pgpt_entries, 0, sizeof(_pgpt_entries));
+ Genode::memset(&_bgpt, 0, sizeof(_bgpt));
+ Genode::memset(_bgpt_entries, 0, sizeof(_bgpt_entries));
+
+ size_t const blocks = (size_t)ENTRIES_SIZE / _block_size;
+
+ /* setup PGPT, BGPT will be synced later */
+ Genode::memcpy(_pgpt.signature, "EFI PART", 8);
+ _pgpt.revision = Gpt::REVISION;
+ _pgpt.size = sizeof(Gpt::Header);
+ _pgpt.lba = Gpt::PGPT_LBA;
+ _pgpt.backup_lba = _block_count - 1;
+ _pgpt.part_lba_start = 2 + blocks;
+ _pgpt.part_lba_end = _block_count - (blocks + 2);
+ _pgpt.guid = Gpt::generate_uuid();
+ _pgpt.gpe_lba = 2;
+ _pgpt.gpe_num = Gpt::MAX_ENTRIES;
+ _pgpt.gpe_size = sizeof(Gpt::Entry);
+
+ _blocks_avail = _pgpt.part_lba_end - _pgpt.part_lba_start;
+
+ _new_gpt = true;
+ }
+
+ /**
+ * Synchronize backup header with changes in the primary header
+ */
+ void _sync_backup_header()
+ {
+ size_t const len = _pgpt.gpe_num * _pgpt.gpe_size;
+ Genode::memcpy(_bgpt_entries, _pgpt_entries, len);
+ Genode::memcpy(&_bgpt, &_pgpt, sizeof(Gpt::Header));
+
+ _bgpt.lba = _pgpt.backup_lba;
+ _bgpt.backup_lba = _pgpt.lba;
+ _bgpt.gpe_lba = _pgpt.part_lba_end + 1;
+ }
+
+ /**
+ * Write given header to Block device
+ *
+ * \param hdr reference to header
+ * \param entries pointer to GPT entry array
+ * \param primary flag to indicate which header to write, primary if true
+ * and backup when false
+ *
+ * \return true when successful, otherwise false
+ */
+ bool _write_header(Gpt::Header const &hdr, Gpt::Entry const *entries, bool primary)
+ {
+ using namespace Util;
+
+ try {
+ Block::sector_t const hdr_lba = primary
+ ? 1 : _pgpt.backup_lba;
+ Block_io hdr_io(_block, _block_size, hdr_lba, 1, true,
+ &hdr, sizeof(Gpt::Header));
+
+ size_t const len = hdr.gpe_num * hdr.gpe_size;
+ size_t const blocks = len / _block_size;
+ Block::sector_t const entries_lba = primary
+ ? hdr_lba + 1
+ : _block_count - (blocks + 1);
+ Block_io entries_io(_block, _block_size, entries_lba, blocks, true,
+ entries, len);
+ } catch (Block_io::Io_error) { return false; }
+
+ return true;
+ }
+
+
+ /**
+ * Write protective MBR to Block device
+ *
+ * \return true when successful, otherwise false
+ */
+ bool _write_pmbr()
+ {
+ using namespace Util;
+ try {
+ Block_io pmbr(_block, _block_size, 0, 1, true, &_pmbr, sizeof(_pmbr));
+ } catch (Block_io::Io_error) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Commit in-memory changes to Block device
+ *
+ * \return true if successful, otherwise false
+ */
+ bool _commit_changes()
+ {
+ /* only if in-memory structures changed we want to write */
+ if (!_new_gpt && !_new_geometry) { return true; }
+
+ /* remove stale backup header */
+ if (_new_geometry) { _wipe_old_backup_header(); }
+
+ _sync_backup_header();
+
+ Gpt::update_crc32(_pgpt, _pgpt_entries);
+ Gpt::update_crc32(_bgpt, _bgpt_entries);
+
+ return _write_header(_pgpt, _pgpt_entries, true)
+ && _write_header(_bgpt, _bgpt_entries, false)
+ && (_new_pmbr ? _write_pmbr() : true) ? true : false;
+ }
+
+ /**
+ * Update geometry information, i.e., fill whole Block device
+ */
+ void _update_geometry_information()
+ {
+ if (_pgpt.backup_lba == _block_count - 1) { return; }
+
+ _setup_pmbr();
+
+ _old_backup_hdr_lba = _pgpt.backup_lba;
+
+ size_t const blocks = (size_t)ENTRIES_SIZE / _block_size;
+
+ _pgpt.backup_lba = _block_count - 1;
+ _pgpt.part_lba_end = _block_count - (blocks + 2);
+
+ _new_geometry = true;
+ }
+
+ /*************
+ ** Actions **
+ *************/
+
+ enum {
+ INVALID_ENTRY = ~0u,
+ INVALID_START = 0,
+ INVALID_SIZE = ~0ull,
+ };
+
+ /**
+ * Check if given entry number is in range
+ */
+ uint32_t _check_range(Gpt::Header const &hdr, uint32_t const entry)
+ {
+ return (entry != (uint32_t)INVALID_ENTRY
+ && (entry == 0 || entry > hdr.gpe_num))
+ ? (uint32_t)INVALID_ENTRY : entry;
+ }
+
+ /**
+ * Lookup entry by number or label
+ *
+ * \return pointer to entry if found, otherwise a nullptr is returned
+ */
+ Gpt::Entry *_lookup_entry(Genode::Xml_node node)
+ {
+ Gpt::Label const label = node.attribute_value("label", Gpt::Label());
+ uint32_t const entry =
+ _check_range(_pgpt,
+ node.attribute_value("entry", (uint32_t)INVALID_ENTRY));
+
+ if (entry == INVALID_ENTRY && !label.valid()) {
+ Genode::error("cannot lookup entry, invalid arguments");
+ return nullptr;
+ }
+
+ if (entry != INVALID_ENTRY && label.valid()) {
+ Genode::warning("entry and label given, entry number will be used");
+ }
+
+ Gpt::Entry *e = nullptr;
+
+ if (entry != INVALID_ENTRY) {
+ /* we start at 0 */
+ e = &_pgpt_entries[entry - 1];
+ }
+
+ if (e == nullptr && label.valid()) {
+ e = Gpt::lookup_entry(_pgpt_entries, _pgpt.gpe_num, label);
+ }
+
+ return e;
+ }
+
+ /**
+ * Add new GPT entry
+ *
+ * \param node action node that contains the arguments
+ *
+ * \return true if entry was successfully added, otherwise false
+ */
+ bool _do_add(Genode::Xml_node node)
+ {
+ bool const add = node.has_attribute("entry");
+ Gpt::Label const label = node.attribute_value("label", Gpt::Label());
+ Gpt::Type const type = node.attribute_value("type", Gpt::Type());
+ uint64_t const size = Util::convert(node.attribute_value("size",
+ Util::Size_string()));
+
+ if (_verbose) {
+ Genode::log(add ? "Add" : "Append", " entry '",
+ label.valid() ? label : "", "' size: ", size);
+ }
+
+ if (!size) {
+ Genode::error("invalid size");
+ return false;
+ }
+
+ /* check if partition will fit */
+ Block::sector_t length = Util::size_to_lba(_block_size, size);
+
+ Block::sector_t lba = node.attribute_value("start", (Block::sector_t)INVALID_START);
+
+ Gpt::Entry *e = nullptr;
+ if (add) {
+ uint32_t const entry = _check_range(_pgpt,
+ node.attribute_value("entry", (uint32_t)INVALID_ENTRY));
+
+ if ( entry == INVALID_ENTRY
+ || lba == INVALID_START
+ || size == INVALID_SIZE) {
+ Genode::error("cannot add entry, invalid arguments");
+ return false;
+ }
+
+ if (length > _blocks_avail) {
+ Genode::error("not enough sectors left (", _blocks_avail,
+ ") to satisfy request");
+ return false;
+ }
+
+ if (_pgpt_entries[entry].valid()) {
+ Genode::error("cannot add already existing entry ", entry);
+ return false;
+ }
+
+ e = &_pgpt_entries[entry-1];
+
+ if (e->valid()) {
+ Genode::error("cannot add existing entry ", entry);
+ return false;
+ }
+
+ } else {
+ /* assume append operation */
+ Entry const *last = Gpt::find_last_valid(_pgpt, _pgpt_entries);
+
+ e = last ? Gpt::find_next_free(_pgpt, _pgpt_entries, last)
+ : Gpt::find_free(_pgpt, _pgpt_entries);
+
+ if (!e) {
+ Genode::error("cannot append partition, no free entry found");
+ return false;
+ }
+
+ if (lba != INVALID_START) {
+ Genode::warning("will ignore start LBA in append mode");
+ }
+
+ lba = last ? last->lba_end + 1 : _pgpt.part_lba_start;
+ if (lba == INVALID_START) {
+ Genode::error("cannot find start LBA");
+ return false;
+ }
+
+ /* limit length to available blocks */
+ if (length == (~0ull / _block_size)) {
+ length = Gpt::gap_length(_pgpt, _pgpt_entries, last ? last : nullptr);
+ }
+
+ /* account for alignment */
+ uint64_t const align = _entry_alignment / _block_size;
+ if (length < align) {
+ Genode::error("cannot satisfy alignment constraints");
+ return false;
+ }
+ }
+
+ Gpt::Uuid const type_uuid = Gpt::type_to_uuid(type);
+
+ e->type = type_uuid;
+ e->guid = Gpt::generate_uuid();
+ e->lba_start = Util::align_start(_block_size,
+ _entry_alignment, lba);
+ uint64_t const lba_start = e->lba_start;
+ if (lba_start != lba) {
+ Genode::warning("start LBA ", lba, " set to ", lba_start,
+ " due to alignment constraints");
+ }
+ e->lba_end = e->lba_start + (length - 1);
+
+ if (label.valid()) { e->write_name(label); }
+
+ _blocks_avail -= length;
+ return true;
+ }
+
+ /**
+ * Delete existing GPT entry
+ *
+ * \param node action node that contains the arguments
+ *
+ * \return true if entry was successfully added, otherwise false
+ */
+ bool _do_delete(Genode::Xml_node node)
+ {
+ Gpt::Entry *e = _lookup_entry(node);
+ if (!e) { return false; }
+
+ if (_verbose) {
+ char tmp[48];
+ e->read_name(tmp, sizeof(tmp));
+ uint32_t const num = Gpt::entry_num(_pgpt_entries, e);
+ Genode::log("Delete entry ", num, " '", (char const*)tmp, "'");
+ }
+
+ _blocks_avail += e->length();
+
+ Genode::memset(e, 0, sizeof(Gpt::Entry));
+ return true;
+ }
+
+ /**
+ * Update existing GPT entry
+ *
+ * \param node action node that contains the arguments
+ *
+ * \return true if entry was successfully added, otherwise false
+ */
+ bool _do_modify(Genode::Xml_node node)
+ {
+ using namespace Genode;
+
+ Gpt::Entry *e = _lookup_entry(node);
+
+ if (!e) {
+ Genode::error("could not lookup entry");
+ return false;
+ }
+
+ if (_verbose) {
+ char tmp[48];
+ e->read_name(tmp, sizeof(tmp));
+ uint32_t const num = Gpt::entry_num(_pgpt_entries, e);
+ Genode::log("Modify entry ", num, " '", (char const*)tmp, "'");
+ }
+
+ uint64_t const new_size = Util::convert(node.attribute_value("new_size",
+ Util::Size_string()));
+ if (new_size) {
+ bool const fill = new_size == ~0ull;
+
+ uint64_t length = fill ? Gpt::gap_length(_pgpt, _pgpt_entries, e)
+ : Util::size_to_lba(_block_size, new_size);
+
+ if (length == 0) {
+ error("cannot modify: ", fill ? "no space left"
+ : "invalid length");
+ return false;
+ }
+
+ uint64_t const old_length = e->length();
+ uint64_t const new_length = length + (fill ? old_length : 0);
+ uint64_t const expand = new_length > old_length ? new_length - old_length : 0;
+
+ if (expand && expand > _blocks_avail) {
+ Genode::error("cannot modify: new length ", expand, " too large");
+ return false;
+ }
+
+ if (!expand) { _blocks_avail += (old_length - new_length); }
+ else { _blocks_avail -= (new_length - old_length); }
+
+ /* XXX overlapping check anyone? */
+ e->lba_end = e->lba_start + new_length - 1;
+ }
+
+ Gpt::Label const new_label = node.attribute_value("new_label", Gpt::Label());
+ if (new_label.valid()) { e->write_name(new_label); }
+
+ Gpt::Type const new_type = node.attribute_value("new_type", Gpt::Type());
+ if (new_type.valid()) {
+ try {
+ Gpt::Uuid type_uuid = Gpt::type_to_uuid(new_type);
+ memcpy(&e->type, &type_uuid, sizeof(Gpt::Uuid));
+ } catch (...) {
+ warning("could not update invalid type");
+ }
+ }
+
+ /* XXX should we generate a new GUID? */
+ // e->guid = Gpt::generate_uuid();
+ return true;
+ }
+
+ /**
+ * Constructor
+ *
+ * \param block reference to underlying Block::Connection
+ * \param config copy of config Xml_node
+ *
+ * \throw Io_error
+ */
+ Writer(Block::Connection &block, Genode::Xml_node config) : _block(block)
+ {
+ _block.info(&_block_count, &_block_size, &_block_ops);
+
+ if (!_block_ops.supported(Block::Packet_descriptor::WRITE)) {
+ Genode::error("cannot write to Block session");
+ throw Io_error();
+ }
+
+ /* initial config read in */
+ _handle_config(config);
+
+ /*
+ * In case of wiping, end here.
+ */
+ if (_wipe) { return; }
+
+ /*
+ * Read and validate the primary GPT header and its entries first
+ * and check the backup GPT header afterwards.
+ */
+ if (!_initialize) {
+ _fill_and_check_header(true);
+ _fill_and_check_header(false);
+
+ if (_update_geometry) {
+ Genode::log("Update geometry information");
+ _update_geometry_information();
+ }
+
+ /* set available blocks */
+ uint64_t const total = _pgpt.part_lba_end - _pgpt.part_lba_start;
+ _blocks_avail = total - Gpt::get_used_blocks(_pgpt, _pgpt_entries);
+ }
+ }
+
+ /**
+ * Execute actions specified in config
+ *
+ * \return true if actions were executed successfully, otherwise
+ * false
+ */
+ bool execute_actions()
+ {
+ if (_wipe) { return _wipe_tables(); }
+
+ if (_initialize) { _initialize_tables(); }
+
+ try {
+ Genode::Xml_node actions = _config->sub_node("actions");
+
+ actions.for_each_sub_node([&] (Genode::Xml_node node) {
+ bool result = false;
+
+ if (node.has_type("add")) {
+ result = _do_add(node);
+ } else if (node.has_type("delete")) {
+ result = _do_delete(node);
+ } else if (node.has_type("modify")) {
+ result = _do_modify(node);
+ } else {
+ Genode::warning("skipping invalid action");
+ return;
+ }
+
+ if (!result) { throw -1; }
+
+ _new_gpt |= result;
+ });
+ } catch (...) { return false; }
+
+ /* finally write changes to disk */
+ return _commit_changes();
+ }
+};
+
+
+struct Main
+{
+ Genode::Env &_env;
+ Genode::Heap _heap { _env.ram(), _env.rm() };
+
+ Genode::Attached_rom_dataspace _config_rom { _env, "config" };
+
+ enum { TX_BUF_SIZE = 128u << 10, };
+ Genode::Allocator_avl _block_alloc { &_heap };
+ Block::Connection _block { _env, &_block_alloc, TX_BUF_SIZE };
+
+ Genode::Constructible _writer { };
+
+ Main(Genode::Env &env) : _env(env)
+ {
+ if (!_config_rom.valid()) {
+ Genode::error("invalid config");
+ _env.parent().exit(1);
+ return;
+ }
+
+ Util::init_random(_heap);
+
+ try {
+ _writer.construct(_block, _config_rom.xml());
+ } catch (...) {
+ _env.parent().exit(1);
+ return;
+ }
+
+ bool const success = _writer->execute_actions();
+ _env.parent().exit(success ? 0 : 1);
+ }
+};
+
+
+void Component::construct(Genode::Env &env) { static Main main(env); }
diff --git a/repos/gems/src/app/gpt_write/pmbr.h b/repos/gems/src/app/gpt_write/pmbr.h
new file mode 100644
index 0000000000..84981dc67a
--- /dev/null
+++ b/repos/gems/src/app/gpt_write/pmbr.h
@@ -0,0 +1,48 @@
+/*
+ * \brief Protective MBR partition table definitions
+ * \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 _PMBR_H_
+#define _PMBR_H_
+
+/* Genode includes */
+#include
+
+
+namespace Protective_mbr
+{
+ enum { TYPE_PROTECTIVE = 0xEE, };
+
+ /**
+ * Partition table entry format
+ */
+ struct Partition
+ {
+ Genode::uint8_t unused[4] { };
+ Genode::uint8_t type { };
+ Genode::uint8_t unused2[3] { };
+ Genode::uint32_t lba { };
+ Genode::uint32_t sectors { };
+ } __attribute__((packed));
+
+ /**
+ * Master boot record header
+ */
+ struct Header
+ {
+ Genode::uint8_t unused[446] { };
+ Partition partitions[4] { };
+ Genode::uint16_t magic { 0xaa55 };
+ } __attribute__((packed));
+}
+
+#endif /* _PMBR_H_ */
diff --git a/repos/gems/src/app/gpt_write/target.mk b/repos/gems/src/app/gpt_write/target.mk
new file mode 100644
index 0000000000..21e6aa13df
--- /dev/null
+++ b/repos/gems/src/app/gpt_write/target.mk
@@ -0,0 +1,4 @@
+TARGET := gpt_write
+LIBS := base jitterentropy
+SRC_CC := main.cc util.cc
+INC_DIR := $(PRG_DIR)
diff --git a/repos/gems/src/app/gpt_write/util.cc b/repos/gems/src/app/gpt_write/util.cc
new file mode 100644
index 0000000000..1ed77d8bd0
--- /dev/null
+++ b/repos/gems/src/app/gpt_write/util.cc
@@ -0,0 +1,227 @@
+/*
+ * \brief Random utility
+ * \author Josef Soentgen
+ * \date 2018-05-05
+ */
+
+/*
+ * 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.
+ */
+
+/* Genode includes */
+#include
+
+/* local includes */
+#include
+
+/* library includes */
+#include
+
+
+static struct rand_data *_ec_stir;
+
+
+/**
+ * Initialize random back end
+ *
+ * \param alloc reference to allocator used internally
+ *
+ * \throw Init_random_failed
+ */
+void Util::init_random(Genode::Allocator &alloc)
+{
+ struct Init_random_failed : Genode::Exception { };
+
+ /* initialize private allocator backend */
+ jitterentropy_init(alloc);
+
+ int err = jent_entropy_init();
+ if (err) {
+ Genode::error("jitterentropy library could not be initialized!");
+ throw Init_random_failed();
+ }
+
+ /* use the default behaviour as specified in jitterentropy(3) */
+ _ec_stir = jent_entropy_collector_alloc(0, 0);
+ if (!_ec_stir) {
+ Genode::error("jitterentropy could not allocate entropy collector!");
+ throw Init_random_failed();
+ }
+}
+
+
+/**
+ * Fill buffer with requested number of random bytes
+ *
+ * \param dest pointer to destination buffer
+ * \param len size of the destination buffer
+ *
+ * \throw Could_not_harvest_enough_randomness
+ */
+void Util::get_random(Genode::uint8_t *dest, Genode::size_t len)
+{
+ struct Could_not_harvest_enough_randomness : Genode::Exception { };
+
+ if (jent_read_entropy(_ec_stir, (char*)dest, len) < 0) {
+ throw Could_not_harvest_enough_randomness();
+ }
+}
+
+
+/**
+ * Convert size string
+ *
+ * \param size reference to size string
+ *
+ * \return converted size in bytes
+ */
+Genode::uint64_t Util::convert(Util::Size_string const &size)
+{
+ if (!size.valid()) { return 0; }
+
+ Genode::uint64_t length = 0;
+ enum { MAX_SIZE = ~0ull, };
+ if (size == "max") {
+ length = MAX_SIZE;
+ } else {
+ Util::Number_of_bytes bytes;
+ Util::ascii_to(size.string(), bytes);
+ length = bytes;
+ }
+
+ return length;
+}
+
+
+/**
+ * Align LBA at alignment boundary
+ *
+ * \param block_size used to calculate number of LBAs
+ * \param alignment alignment in number of bytes
+ * \param lba start LBA
+ *
+ * \return returns aligned start LBA
+ */
+Block::sector_t Util::align_start(Genode::size_t block_size,
+ Genode::size_t alignment,
+ Block::sector_t lba)
+{
+ struct Invalid_alignment : Genode::Exception { };
+ if (alignment < block_size || !block_size || !alignment) {
+ throw Invalid_alignment();
+ }
+
+ Block::sector_t const blocks = alignment / block_size;
+ return Genode::align_addr(lba, Genode::log2(blocks));
+}
+
+
+/**
+ * Convert size in bytes to number of LBAs
+ */
+Block::sector_t Util::size_to_lba(Genode::size_t block_size, Genode::uint64_t size)
+{
+ /* XXX align/round-down etc. */
+ return size / block_size;
+}
+
+
+/**
+ * Simple bitwise CRC32 checking
+ *
+ * \param buf pointer to buffer containing data
+ * \param size length of buffer in bytes
+ *
+ * \return CRC32 checksum of data
+ */
+Genode::uint32_t Util::crc32(void const * const buf, Genode::size_t size)
+{
+ Genode::uint8_t const *p = static_cast(buf);
+ Genode::uint32_t crc = ~0U;
+
+ while (size--) {
+ crc ^= *p++;
+ for (Genode::uint32_t j = 0; j < 8; j++)
+ crc = (-Genode::int32_t(crc & 1) & 0xedb88320) ^ (crc >> 1);
+ }
+
+ return crc ^ ~0U;
+}
+
+
+/**
+ * Extract all valid ASCII characters from UTF-16LE buffer
+ *
+ * The function operates in a rather crude way and just tries to extract all
+ * characters < 128, even non-printable ones.
+ *
+ * \param dest pointer to destination buffer
+ * \param dest_len length of the destination buffer in bytes
+ * \param src pointer to source buffer
+ * \param dest_len length of the source buffer in 2 bytes
+ *
+ * \return length of resulting ASCII string
+ */
+Genode::size_t Util::extract_ascii(char *dest, size_t dest_len,
+ uint16_t const *src, size_t src_len)
+{
+ char *p = dest;
+ size_t j = 0;
+ size_t i = 0;
+
+ for (size_t u = 0; u < src_len && src[u] != 0; u++) {
+ uint32_t utfchar = src[i++];
+
+ if ((utfchar & 0xf800) == 0xd800) {
+ unsigned int c = src[i];
+ if ((utfchar & 0x400) != 0 || (c & 0xfc00) != 0xdc00) {
+ utfchar = 0xfffd;
+ } else {
+ i++;
+ }
+ }
+
+ p[j] = (utfchar < 0x80) ? utfchar : '.';
+ /* leave space for NUL */
+ if (++j == dest_len - 1) { break; }
+ }
+
+ p[j] = 0;
+ return j;
+}
+
+
+/**
+ * Convert printable ASCII characters to UTF-16LE
+ *
+ * The function operates in a rather crude way and will
+ * truncate the input string if it does not fit.
+ *
+ * \param dest pointer to destination buffer
+ * \param dest_len length of the destination buffer in 16bit words
+ * \param src pointer to source buffer
+ * \param dest_len length of the source buffer in 8bit words
+ *
+ * \return length of resulting UTF-16 string
+ */
+Genode::size_t Util::convert_ascii(uint16_t *dest, size_t dest_len,
+ uint8_t const *src, size_t src_len)
+{
+ Genode::memset(dest, 0, dest_len * sizeof(uint16_t));
+
+ if (src_len / sizeof(uint16_t) > dest_len) {
+ Genode::warning("input too long, will be truncated");
+ src_len = dest_len;
+ }
+
+ size_t i = 0;
+ for (; i < src_len; i++) {
+ uint16_t const utfchar = src[i];
+ dest[i] = utfchar;
+ }
+
+ return i;
+}
diff --git a/repos/gems/src/app/gpt_write/util.h b/repos/gems/src/app/gpt_write/util.h
new file mode 100644
index 0000000000..480f050e41
--- /dev/null
+++ b/repos/gems/src/app/gpt_write/util.h
@@ -0,0 +1,169 @@
+/*
+ * \brief GPT utils
+ * \author Josef Soentgen
+ * \date 2018-05-01
+ */
+
+/*
+ * 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 _UTIL_H_
+#define _UTIL_H_
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+
+
+namespace Util {
+
+ using namespace Genode;
+
+ void init_random(Genode::Allocator &);
+ void get_random(uint8_t *dest, size_t len);
+
+ using Label = Genode::String<128>;
+
+ using Size_string = Genode::String<64>;
+ uint64_t convert(Size_string const &);
+
+ using sector_t = Block::sector_t;
+
+ sector_t align_start(size_t, size_t, sector_t);
+ sector_t size_to_lba(size_t, uint64_t);
+
+ struct Block_io;
+
+ uint32_t crc32(void const * const, size_t);
+ size_t extract_ascii(char *, size_t, uint16_t const *, size_t);
+ size_t convert_ascii(uint16_t *, size_t, uint8_t const *, size_t);
+
+ /*
+ * Wrapper to get suffixed uint64_t values
+ */
+ class Number_of_bytes
+ {
+ uint64_t _n;
+
+ public:
+
+ /**
+ * Default constructor
+ */
+ Number_of_bytes() : _n(0) { }
+
+ /**
+ * Constructor, to be used implicitly via assignment operator
+ */
+ Number_of_bytes(Genode::uint64_t n) : _n(n) { }
+
+ /**
+ * Convert number of bytes to 'size_t' value
+ */
+ operator Genode::uint64_t() const { return _n; }
+
+ void print(Output &output) const
+ {
+ using Genode::print;
+
+ enum { KB = 1024UL, MB = KB*1024UL, GB = MB*1024UL };
+
+ if (_n == 0) print(output, 0);
+ else if (_n % GB == 0) print(output, _n/GB, "G");
+ else if (_n % MB == 0) print(output, _n/MB, "M");
+ else if (_n % KB == 0) print(output, _n/KB, "K");
+ else print(output, _n);
+ }
+ };
+
+ inline size_t ascii_to(const char *s, Number_of_bytes &result)
+ {
+ unsigned long res = 0;
+
+ /* convert numeric part of string */
+ int i = ascii_to_unsigned(s, res, 0);
+
+ /* handle suffixes */
+ if (i > 0)
+ switch (s[i]) {
+ case 'G': res *= 1024;
+ case 'M': res *= 1024;
+ case 'K': res *= 1024; i++;
+ default: break;
+ }
+
+ result = res;
+ return i;
+ }
+};
+
+
+/*
+ * Block_io wraps a Block::Connection for synchronous operations
+ */
+struct Util::Block_io
+{
+ struct Io_error : Genode::Exception { };
+
+ using Packet_descriptor = Block::Packet_descriptor;
+
+ Block::Connection &_block;
+ Packet_descriptor _p;
+
+ /**
+ * Constructor
+ *
+ * \param block reference to underlying Block::Connection
+ * \param block_size logical block size of the Block::Connection
+ * \param lba LBA to start access from
+ * \param count number of LBAs to access
+ * \param write set type of operation, write if true, read
+ * if false
+ *
+ * \throw Io_error
+ */
+ Block_io(Block::Connection &block, size_t block_size,
+ sector_t lba, size_t count,
+ bool write = false, void const *data = nullptr, size_t len = 0)
+ :
+ _block(block),
+ _p(_block.tx()->alloc_packet(block_size * count),
+ write ? Packet_descriptor::WRITE
+ : Packet_descriptor::READ, lba, count)
+ {
+ if (write) {
+ if (data && len) {
+ void *p = addr();
+ Genode::memcpy(p, data, len);
+ } else {
+ Genode::error("invalid data for write");
+ throw Io_error();
+ }
+ }
+
+ _block.tx()->submit_packet(_p);
+ _p = _block.tx()->get_acked_packet();
+ if (!_p.succeeded()) {
+ Genode::error("could not ", write ? "write" : "read",
+ " block-range [", _p.block_number(), ",",
+ _p.block_number() + count, ")");
+ _block.tx()->release_packet(_p);
+ throw Io_error();
+ }
+ }
+
+ ~Block_io() { _block.tx()->release_packet(_p); }
+
+ template T addr()
+ {
+ return reinterpret_cast(_block.tx()->packet_content(_p));
+ }
+};
+
+#endif /* _UTIL_H_ */