diff --git a/repos/gems/include/cbe/check/library.h b/repos/gems/include/cbe/check/library.h new file mode 100644 index 0000000000..3eae15d292 --- /dev/null +++ b/repos/gems/include/cbe/check/library.h @@ -0,0 +1,59 @@ +/* + * \brief Integration of the Consistent Block Encrypter (CBE) + * \author Martin Stein + * \author Josef Soentgen + * \date 2020-11-10 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _CBE__CHECK__LIBRARY_H_ +#define _CBE__CHECK__LIBRARY_H_ + +/* CBE includes */ +#include +#include + + +extern "C" void cbe_check_cxx_init(); +extern "C" void cbe_check_cxx_final(); + + +namespace Cbe_check { + + class Library; + + Genode::uint32_t object_size(Library const &); + +} + +struct Cbe_check::Library : Cbe::Spark_object<46160> +{ + Library(); + + bool client_request_acceptable() const; + + void submit_client_request(Cbe::Request const &request); + + Cbe::Request peek_completed_client_request() const; + + void drop_completed_client_request(Cbe::Request const &req); + + void execute(Cbe::Io_buffer const &io_buf); + + bool execute_progress() const; + + void io_request_completed(Cbe::Io_buffer::Index const &data_index, + bool const success); + + void has_io_request(Cbe::Request &, Cbe::Io_buffer::Index &) const; + + void io_request_in_progress(Cbe::Io_buffer::Index const &data_index); +}; + +#endif /* _CBE__CHECK__LIBRARY_H_ */ diff --git a/repos/gems/include/cbe/crypto/interface.h b/repos/gems/include/cbe/crypto/interface.h new file mode 100644 index 0000000000..40675dd7da --- /dev/null +++ b/repos/gems/include/cbe/crypto/interface.h @@ -0,0 +1,119 @@ +/* + * \brief Integration of the Consistent Block Encrypter (CBE) + * \author Martin Stein + * \author Josef Soentgen + * \date 2020-11-10 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _INCLUDE__CBE__CRYPTO__INTERFACE_H_ +#define _INCLUDE__CBE__CRYPTO__INTERFACE_H_ + +/* Genode includes */ +#include +#include + + +namespace Cbe_crypto { + + using uint32_t = Genode::uint32_t; + using uint64_t = Genode::uint64_t; + using size_t = Genode::size_t; + + struct Interface; + + Interface &get_interface(); + + enum { BLOCK_SIZE = 4096u }; + +} /* namespace Cbe_crypto */ + + +struct Cbe_crypto::Interface +{ + struct Buffer_too_small : Genode::Exception { }; + struct Key_value_size_mismatch : Genode::Exception { }; + + struct Complete_request + { + bool const valid; + uint64_t const block_number; + }; + + struct Slots + { + enum { NUM_SLOTS = 2, }; + uint32_t _store[NUM_SLOTS] { }; + + bool store(uint32_t const id) + { + for (uint32_t &slot : _store) { + if (slot == 0) { + slot = id; + return true; + } + } + return false; + } + + void remove(uint32_t const id) + { + for (uint32_t &slot : _store) { + if (slot == id) { + slot = 0; + return; + } + } + } + + template + void for_each_key(FN const &func) + { + for (uint32_t const slot : _store) { + if (slot != 0) { + func(slot); + } + } + } + }; + + Slots _slots { }; + + virtual ~Interface() { } + + template + void for_each_key(FN const &func) + { + _slots.for_each_key(func); + } + + virtual bool execute() = 0; + + virtual bool add_key(uint32_t const id, + char const *value, + size_t value_len) = 0; + + virtual bool remove_key(uint32_t const id) = 0; + + virtual bool submit_encryption_request(uint64_t const block_number, + uint32_t const key_id, + char const *src, + size_t const src_len) = 0; + + virtual Complete_request encryption_request_complete(char *dst, size_t const dst_len) = 0; + + virtual bool submit_decryption_request(uint64_t const block_number, + uint32_t const key_id, + char const *src, + size_t const src_len) = 0; + + virtual Complete_request decryption_request_complete(char *dst, size_t dst_len) = 0; +}; + +#endif /* _INCLUDE__CBE__CRYPTO__INTERFACE_H_ */ diff --git a/repos/gems/include/cbe/dump/configuration.h b/repos/gems/include/cbe/dump/configuration.h new file mode 100644 index 0000000000..e21705e67e --- /dev/null +++ b/repos/gems/include/cbe/dump/configuration.h @@ -0,0 +1,101 @@ +/* + * \brief Integration of the Consistent Block Encrypter (CBE) + * \author Martin Stein + * \author Josef Soentgen + * \date 2020-11-10 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _CBE__DUMP__CONFIGURATION_H_ +#define _CBE__DUMP__CONFIGURATION_H_ + +/* Genode includes */ +#include + +namespace Cbe_dump { class Configuration; } + +class Cbe_dump::Configuration +{ + private: + + bool _unused_nodes; + Genode::uint32_t _max_superblocks; + Genode::uint32_t _max_snapshots; + bool _vbd; + bool _vbd_pba_filter_enabled; + Genode::uint64_t _vbd_pba_filter; + bool _vbd_vba_filter_enabled; + Genode::uint64_t _vbd_vba_filter; + bool _free_tree; + bool _meta_tree; + bool _hashes; + + public: + + Configuration (Genode::Xml_node const &node) + : + _unused_nodes { node.attribute_value("unused_nodes", true) }, + _max_superblocks { node.attribute_value("max_superblocks", ~(Genode::uint32_t)0) }, + _max_snapshots { node.attribute_value("max_snapshots", ~(Genode::uint32_t)0) }, + _vbd { node.attribute_value("vbd", true) }, + _vbd_pba_filter_enabled { node.attribute_value("vbd_pba_filter_enabled", false) }, + _vbd_pba_filter { node.attribute_value("vbd_pba_filter", (Genode::uint64_t)0) }, + _vbd_vba_filter_enabled { node.attribute_value("vbd_vba_filter_enabled", false) }, + _vbd_vba_filter { node.attribute_value("vbd_vba_filter", (Genode::uint64_t)0) }, + _free_tree { node.attribute_value("free_tree", true) }, + _meta_tree { node.attribute_value("meta_tree", true) }, + _hashes { node.attribute_value("hashes", true) } + { } + + Configuration (Configuration const &other) + : + _unused_nodes { other._unused_nodes }, + _max_superblocks { other._max_superblocks }, + _max_snapshots { other._max_snapshots }, + _vbd { other._vbd }, + _vbd_pba_filter_enabled { other._vbd_pba_filter_enabled }, + _vbd_pba_filter { other._vbd_pba_filter }, + _vbd_vba_filter_enabled { other._vbd_vba_filter_enabled }, + _vbd_vba_filter { other._vbd_vba_filter }, + _free_tree { other._free_tree }, + _meta_tree { other._meta_tree }, + _hashes { other._hashes } + { } + + bool unused_nodes() const { return _unused_nodes; } + Genode::uint32_t max_superblocks() const { return _max_superblocks; } + Genode::uint32_t max_snapshots() const { return _max_snapshots; } + bool vbd() const { return _vbd; } + bool vbd_pba_filter_enabled() const { return _vbd_pba_filter_enabled; } + Genode::uint64_t vbd_pba_filter() const { return _vbd_pba_filter; } + bool vbd_vba_filter_enabled() const { return _vbd_vba_filter_enabled; } + Genode::uint64_t vbd_vba_filter() const { return _vbd_vba_filter; } + bool free_tree() const { return _free_tree; } + bool meta_tree() const { return _meta_tree; } + bool hashes() const { return _hashes; } + + void print(Genode::Output &out) const + { + Genode::print(out, + "unused_nodes=", _unused_nodes , + " max_superblocks=", _max_superblocks , + " max_snapshots=", _max_snapshots , + " vbd=", _vbd , + " vbd_pba_filter_enabled=", _vbd_pba_filter_enabled, + " vbd_pba_filter=", _vbd_pba_filter , + " vbd_vba_filter_enabled=", _vbd_vba_filter_enabled, + " vbd_vba_filter=", _vbd_vba_filter , + " free_tree=", _free_tree , + " meta_tree=", _meta_tree , + " hashes=", _hashes ); + } + +} __attribute__((packed)); + +#endif /* _CBE__DUMP__CONFIGURATION_H_ */ diff --git a/repos/gems/include/cbe/dump/library.h b/repos/gems/include/cbe/dump/library.h new file mode 100644 index 0000000000..adf3c6d59f --- /dev/null +++ b/repos/gems/include/cbe/dump/library.h @@ -0,0 +1,63 @@ +/* + * \brief Integration of the Consistent Block Encrypter (CBE) + * \author Martin Stein + * \author Josef Soentgen + * \date 2020-11-10 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _CBE__DUMP__LIBRARY_H_ +#define _CBE__DUMP__LIBRARY_H_ + +/* CBE includes */ +#include +#include + +/* CBE dump includes */ +#include + + +extern "C" void cbe_dump_cxx_init(); +extern "C" void cbe_dump_cxx_final(); + + +namespace Cbe_dump { + + class Library; + + Genode::uint32_t object_size(Library const &); + +} + +struct Cbe_dump::Library : Cbe::Spark_object<49240> +{ + Library(); + + bool client_request_acceptable() const; + + void submit_client_request(Cbe::Request const &request, + Configuration const &cfg); + + Cbe::Request peek_completed_client_request() const; + + void drop_completed_client_request(Cbe::Request const &req); + + void execute(Cbe::Io_buffer const &io_buf); + + bool execute_progress() const; + + void io_request_completed(Cbe::Io_buffer::Index const &data_index, + bool const success); + + void has_io_request(Cbe::Request &, Cbe::Io_buffer::Index &) const; + + void io_request_in_progress(Cbe::Io_buffer::Index const &data_index); +}; + +#endif /* _CBE__DUMP__LIBRARY_H_ */ diff --git a/repos/gems/include/cbe/init/configuration.h b/repos/gems/include/cbe/init/configuration.h new file mode 100644 index 0000000000..3b7e0fa363 --- /dev/null +++ b/repos/gems/include/cbe/init/configuration.h @@ -0,0 +1,100 @@ +/* + * \brief Integration of the Consistent Block Encrypter (CBE) + * \author Martin Stein + * \author Josef Soentgen + * \date 2020-11-10 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _CBE__INIT__CONFIGURATION_H_ +#define _CBE__INIT__CONFIGURATION_H_ + +/* Genode includes */ +#include + +namespace Cbe_init { class Configuration; } + +class Cbe_init::Configuration +{ + private: + + Genode::uint64_t _vbd_nr_of_lvls { 0 }; + Genode::uint64_t _vbd_nr_of_children { 0 }; + Genode::uint64_t _vbd_nr_of_leafs { 0 }; + Genode::uint64_t _ft_nr_of_lvls { 0 }; + Genode::uint64_t _ft_nr_of_children { 0 }; + Genode::uint64_t _ft_nr_of_leafs { 0 }; + + public: + + struct Invalid : Genode::Exception { }; + + Configuration (Genode::Xml_node const &node) + { + node.with_sub_node("virtual-block-device", + [&] (Genode::Xml_node const &vbd) + { + _vbd_nr_of_lvls = + vbd.attribute_value("nr_of_levels", (Genode::uint64_t)0); + _vbd_nr_of_children = + vbd.attribute_value("nr_of_children", (Genode::uint64_t)0); + _vbd_nr_of_leafs = + vbd.attribute_value("nr_of_leafs", (Genode::uint64_t)0); + }); + node.with_sub_node("free-tree", + [&] (Genode::Xml_node const &ft) + { + _ft_nr_of_lvls = + ft.attribute_value("nr_of_levels", (Genode::uint64_t)0); + _ft_nr_of_children = + ft.attribute_value("nr_of_children", (Genode::uint64_t)0); + _ft_nr_of_leafs = + ft.attribute_value("nr_of_leafs", (Genode::uint64_t)0); + }); + if (_vbd_nr_of_lvls == 0 || + _vbd_nr_of_children == 0 || + _vbd_nr_of_leafs == 0 || + _ft_nr_of_lvls == 0 || + _ft_nr_of_children == 0 || + _ft_nr_of_leafs == 0) + { + throw Invalid(); + } + } + + Configuration (Configuration const &other) + { + _vbd_nr_of_lvls = other._vbd_nr_of_lvls ; + _vbd_nr_of_children = other._vbd_nr_of_children; + _vbd_nr_of_leafs = other._vbd_nr_of_leafs ; + _ft_nr_of_lvls = other._ft_nr_of_lvls ; + _ft_nr_of_children = other._ft_nr_of_children ; + _ft_nr_of_leafs = other._ft_nr_of_leafs ; + } + + Genode::uint64_t vbd_nr_of_lvls () const { return _vbd_nr_of_lvls ; } + Genode::uint64_t vbd_nr_of_children () const { return _vbd_nr_of_children; } + Genode::uint64_t vbd_nr_of_leafs () const { return _vbd_nr_of_leafs ; } + Genode::uint64_t ft_nr_of_lvls () const { return _ft_nr_of_lvls ; } + Genode::uint64_t ft_nr_of_children () const { return _ft_nr_of_children ; } + Genode::uint64_t ft_nr_of_leafs () const { return _ft_nr_of_leafs ; } + + void print(Genode::Output &out) const + { + Genode::print(out, + "vbd=(lvls=", _vbd_nr_of_lvls, + " children=", _vbd_nr_of_children, + " leafs=", _vbd_nr_of_leafs, ")", + " ft=(lvls=", _ft_nr_of_lvls, + " children=", _ft_nr_of_children, + " leafs=", _ft_nr_of_leafs, ")"); + } +}; + +#endif /* _CBE__INIT__CONFIGURATION_H_ */ diff --git a/repos/gems/include/cbe/init/library.h b/repos/gems/include/cbe/init/library.h new file mode 100644 index 0000000000..0251988d53 --- /dev/null +++ b/repos/gems/include/cbe/init/library.h @@ -0,0 +1,124 @@ +/* + * \brief Integration of the Consistent Block Encrypter (CBE) + * \author Martin Stein + * \author Josef Soentgen + * \date 2020-11-10 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _CBE__INIT__LIBRARY_H_ +#define _CBE__INIT__LIBRARY_H_ + +/* CBE includes */ +#include +#include + + +extern "C" void cbe_init_cxx_init(); +extern "C" void cbe_init_cxx_final(); + + +namespace Cbe_init { + + class Library; + + Genode::uint32_t object_size(Library const &); + +} + +struct Cbe_init::Library : Cbe::Spark_object<60960> +{ + /* + * Ada/SPARK compatible bindings + */ + + void _peek_generated_ta_request(Cbe::Trust_anchor_request &) const; + void _peek_generated_ta_sb_hash(Cbe::Trust_anchor_request const &, Cbe::Hash &) const; + void _peek_generated_ta_key_value_plaintext(Cbe::Trust_anchor_request const &, + Cbe::Key_plaintext_value &) const; + void _peek_generated_ta_key_value_ciphertext(Cbe::Trust_anchor_request const &, + Cbe::Key_ciphertext_value &) const; + + Library(); + + bool client_request_acceptable() const; + + void submit_client_request(Cbe::Request const &request, + Genode::uint64_t vbd_max_lvl_idx, + Genode::uint64_t vbd_degree, + Genode::uint64_t vbd_nr_of_leafs, + Genode::uint64_t ft_max_lvl_idx, + Genode::uint64_t ft_degree, + Genode::uint64_t ft_nr_of_leafs); + + Cbe::Request peek_completed_client_request() const; + + void drop_completed_client_request(Cbe::Request const &req); + + void execute(Cbe::Io_buffer &io_buf); + + bool execute_progress() const; + + void io_request_completed(Cbe::Io_buffer::Index const &data_index, + bool const success); + + void has_io_request(Cbe::Request &, Cbe::Io_buffer::Index &) const; + + void io_request_in_progress(Cbe::Io_buffer::Index const &data_index); + + Cbe::Trust_anchor_request peek_generated_ta_request() const + { + Cbe::Trust_anchor_request request { }; + _peek_generated_ta_request(request); + return request; + } + + void drop_generated_ta_request(Cbe::Trust_anchor_request const &request); + + Cbe::Hash peek_generated_ta_sb_hash(Cbe::Trust_anchor_request const &request) const + { + Cbe::Hash hash { }; + _peek_generated_ta_sb_hash(request, hash); + return hash; + } + + void mark_generated_ta_secure_sb_request_complete(Cbe::Trust_anchor_request const &request); + + void mark_generated_ta_create_key_request_complete(Cbe::Trust_anchor_request const &request, + Cbe::Key_plaintext_value const &key); + + Cbe::Key_ciphertext_value peek_generated_ta_key_value_ciphertext(Cbe::Trust_anchor_request const &request) const + { + Cbe::Key_ciphertext_value ck { }; + _peek_generated_ta_key_value_ciphertext(request, ck); + return ck; + } + + Cbe::Key_plaintext_value peek_generated_ta_key_value_plaintext(Cbe::Trust_anchor_request const &request) const + { + Cbe::Key_plaintext_value pk { }; + _peek_generated_ta_key_value_plaintext(request, pk); + return pk; + } + + void mark_generated_ta_decrypt_key_request_complete(Cbe::Trust_anchor_request const &reference, + Cbe::Key_plaintext_value const &key); + + void mark_generated_ta_encrypt_key_request_complete(Cbe::Trust_anchor_request const &request, + Cbe::Key_ciphertext_value const &key); + + void mark_generated_ta_last_sb_hash_request_complete(Cbe::Trust_anchor_request const &, + Cbe::Hash const &) + { + struct Not_supported { }; + throw Not_supported(); + } +}; + +#endif /* _CBE__INIT__LIBRARY_H_ */ diff --git a/repos/gems/include/cbe/library.h b/repos/gems/include/cbe/library.h new file mode 100644 index 0000000000..2b8449be66 --- /dev/null +++ b/repos/gems/include/cbe/library.h @@ -0,0 +1,412 @@ +/* + * \brief Integration of the Consistent Block Encrypter (CBE) + * \author Martin Stein + * \author Josef Soentgen + * \date 2020-11-10 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _CBE_LIBRARY_H_ +#define _CBE_LIBRARY_H_ + +/* Genode includes */ +#include +#include + +/* CBE includes */ +#include +#include + + +extern "C" void cbe_cxx_init(); +extern "C" void cbe_cxx_final(); + + +namespace Cbe { + + using namespace Genode; + + class Library; + + Genode::uint32_t object_size(Library const &); + +} /* namespace Cbe */ + + +class Cbe::Library : public Cbe::Spark_object<353944> +{ + private: + + /* + * Ada/SPARK compatible bindings + * + * Ada functions cannot have out parameters. Hence we call Ada + * procedures that return the 'progress' result as last out parameter. + */ + + void _has_io_request(Request &, Io_buffer::Index &) const; + + void _crypto_add_key_required(Request &, Key &) const; + void _crypto_remove_key_required(Request &, Key::Id &) const; + + void _crypto_cipher_data_required(Request &, Crypto_plain_buffer::Index &) const; + void _crypto_plain_data_required(Request &, Crypto_cipher_buffer::Index &) const; + + void _info(Info &) const; + + void _peek_generated_ta_request(Trust_anchor_request &) const; + void _peek_generated_ta_sb_hash(Trust_anchor_request const &, Hash &) const; + void _peek_generated_ta_key_value_plaintext(Trust_anchor_request const &, Key_plaintext_value &) const; + void _peek_generated_ta_key_value_ciphertext(Trust_anchor_request const &, Key_ciphertext_value &) const; + + public: + + Library(); + + /** + * Get highest virtual-block-address useable by the current active snapshot + * + * \return highest addressable virtual-block-address + */ + Virtual_block_address max_vba() const; + + /** + * Get information about the CBE + * + * \return information structure + */ + Info info() const + { + Info inf { }; + _info(inf); + return inf; + } + + void execute(Io_buffer &io_buf, + Crypto_plain_buffer &crypto_plain_buf, + Crypto_cipher_buffer &crypto_cipher_buf); + + /** + * Return whether the last call to 'execute' has made progress or not + */ + bool execute_progress() const; + + /** + * Check if the CBE can accept a new requeust + * + * \return true if a request can be accepted, otherwise false + */ + bool client_request_acceptable() const; + + /** + * Submit a new request + * + * This method must only be called after executing 'request_acceptable' + * returned true. + * + * \param request block request + */ + void submit_client_request(Request const &request, uint32_t id); + + /** + * Check for any completed request + * + * \return a valid block request will be returned if there is an + * completed request, otherwise an invalid one + */ + Request peek_completed_client_request() const; + + /** + * Drops the completed request + * + * This method must only be called after executing + * 'peek_completed_request' returned a valid request. + * + */ + void drop_completed_client_request(Request const &req); + + /* + * Backend block I/O + */ + + /** + * Submit read request data from the backend block session to the CBE + * + * The given data will be transfered to the CBE. + * + * \param request reference to the request from the CBE + * \param data reference to the data associated with the + * request + * + * \return true if the CBE acknowledged the request + */ + void io_request_completed(Io_buffer::Index const &data_index, + bool const success); + + /** + * Return a write request for the backend block session + * + * \param result valid request in case the is one pending that + * needs data, otherwise an invalid one is returned + */ + Request has_io_request(Io_buffer::Index &data_index) const + { + Request result { }; + _has_io_request(result, data_index); + return result; + } + void has_io_request(Request &req, Io_buffer::Index &data_index) const + { + _has_io_request(req, data_index); + } + + /** + * Obtain data for write request for the backend block session + * + * The CBE will transfer the payload to the given data. + * + * \param request reference to the Block::Request processed + * by the CBE + * \param data reference to the data associated with the + * Request + * + * \return true if the CBE could process the request + */ + void io_request_in_progress(Io_buffer::Index const &data_index); + + void client_transfer_read_data_required(Request &, + uint64_t &, + Crypto_plain_buffer::Index &) const; + + void client_transfer_read_data_in_progress(Crypto_plain_buffer::Index const &); + + void client_transfer_read_data_completed(Crypto_plain_buffer::Index const &, bool); + + void client_transfer_write_data_required(Request &, + uint64_t &, + Crypto_plain_buffer::Index &) const; + + void client_transfer_write_data_in_progress(Crypto_plain_buffer::Index const &); + + void client_transfer_write_data_completed(Crypto_plain_buffer::Index const &, bool); + + /** + * Query list of active snapshots + * + * \param ids reference to destination buffer + */ + void active_snapshot_ids(Active_snapshot_ids &ids) const; + + Request crypto_add_key_required(Key &key) const + { + Request result { }; + _crypto_add_key_required(result, key); + return result; + } + + void crypto_add_key_requested(Request const &req); + + void crypto_add_key_completed(Request const &req); + + Request crypto_remove_key_required(Key::Id &key_id) const + { + Request result { }; + _crypto_remove_key_required(result, key_id); + return result; + } + + void crypto_remove_key_requested(Request const &req); + + void crypto_remove_key_completed(Request const &req); + + /** + * CBE requests encrytion + * + * \param result valid request in case the is one pending that + * needs encrytion, otherwise an invalid one is + * returned + */ + Request crypto_cipher_data_required(Crypto_plain_buffer::Index &data_index) const + { + Request result { }; + _crypto_cipher_data_required(result, data_index); + return result; + } + + /** + * Return plain data for given encryption request + * + * \param request reference to the Block::Request processed + * by the CBE + * \param data reference to the data associated with the + * Block::Request + */ + void crypto_cipher_data_requested( + Crypto_plain_buffer::Index const &data_index); + + /** + * Collect cipher data for given completed encryption request + * + * \param request reference to the Block::Request processed + * by the CBE + * \param data reference to the data associated with the + * Block::Request + * + * \return true if the CBE could obtain the encrypted data, + * otherwise false + */ + void supply_crypto_cipher_data(Crypto_cipher_buffer::Index const &data_index, + bool const data_valid); + + /** + * CBE requests decryption + * + * \param result valid request in case the is one pending that + * needs decrytion, otherwise an invalid one is + * returned + */ + Request crypto_plain_data_required(Crypto_cipher_buffer::Index &data_index) const + { + Request result { }; + _crypto_plain_data_required(result, data_index); + return result; + } + + /** + * Return cipher data for given decryption request + * + * \param request reference to the Block::Request processed + * by the CBE + * \param data reference to the data associated with the + * Block::Request + * + * \return true if the CBE could supply the ciphr data, + * otherwise false + */ + void crypto_plain_data_requested( + Crypto_cipher_buffer::Index const &data_index); + + /** + * Collect plain data for given completed decryption request + * + * \param request reference to the Block::Request processed + * by the CBE + * \param data reference to the data associated with the + * Block::Request + * + * \return true if the CBE could obtain the decrypted data, + * otherwise false + */ + void supply_crypto_plain_data(Crypto_plain_buffer::Index const &data_index, + bool const data_valid); + + /** + * CBE trust anchor request + * + * \return valid TA request in case there is one pending, otherwise an + * invalid one is returned + */ + Trust_anchor_request peek_generated_ta_request() const + { + Trust_anchor_request request { }; + _peek_generated_ta_request(request); + return request; + } + + /** + * Drop generated TA request + * + * \param request reference to the request processed by the TA + */ + void drop_generated_ta_request(Trust_anchor_request const &request); + + /** + * Peek generated TA superblock hash + * + * \param request reference to the request + * \return superblock hash + */ + Hash peek_generated_ta_sb_hash(Trust_anchor_request const &request) const + { + Cbe::Hash hash { }; + _peek_generated_ta_sb_hash(request, hash); + return hash; + } + + /** + * Mark generated TA secure superblock request complete + * + * \param request reference to the request completed by the TA + */ + void mark_generated_ta_secure_sb_request_complete(Trust_anchor_request const &request); + + /** + * Mark generated TA create key request complete + * + * \param request reference to the request completed by the TA + * \param key reference to the key plaintext created by the TA + */ + void mark_generated_ta_create_key_request_complete(Trust_anchor_request const &request, + Key_plaintext_value const &key); + + /** + * Peek generated TA key ciphertext + * + * \param request reference to the request + * \return key ciphertext + */ + Key_ciphertext_value peek_generated_ta_key_value_ciphertext(Trust_anchor_request const &request) const + { + Cbe::Key_ciphertext_value ck { }; + _peek_generated_ta_key_value_ciphertext(request, ck); + return ck; + } + + /** + * Peek generated TA key plaintext + * + * \param request reference to the request + * \return key plaintext + */ + Key_plaintext_value peek_generated_ta_key_value_plaintext(Trust_anchor_request const &request) const + { + Cbe::Key_plaintext_value pk { }; + _peek_generated_ta_key_value_plaintext(request, pk); + return pk; + } + + /** + * Mark generated TA decrypt key request complete + * + * \param request reference to the request completed by the TA + * \param key reference to the key plaintext decrypted by the TA + */ + void mark_generated_ta_decrypt_key_request_complete(Trust_anchor_request const &reference, + Key_plaintext_value const &key); + + /** + * Mark generated TA encrypt key request complete + * + * \param request reference to the request completed by the TA + * \param key reference to the key ciphertext encrypted by the TA + */ + void mark_generated_ta_encrypt_key_request_complete(Trust_anchor_request const &request, + Key_ciphertext_value const &key); + + /** + * Mark generated TA last superblock hash request complete + * + * \param request reference to the request completed by the TA + * \param hash reference to the superblock hash stored in the TA + */ + void mark_generated_ta_last_sb_hash_request_complete(Trust_anchor_request const &request, + Hash const &hash); +}; + +#endif /* _CBE_LIBRARY_H_ */ diff --git a/repos/gems/include/cbe/spark_object.h b/repos/gems/include/cbe/spark_object.h new file mode 100644 index 0000000000..540e5812dd --- /dev/null +++ b/repos/gems/include/cbe/spark_object.h @@ -0,0 +1,65 @@ +/* + * \brief Integration of the Consistent Block Encrypter (CBE) + * \author Martin Stein + * \author Josef Soentgen + * \date 2020-11-10 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _CBE_SPARK_OBJECT_H_ +#define _CBE_SPARK_OBJECT_H_ + +/* Genode includes */ +#include +#include +#include + +namespace Cbe { + + /** + * Opaque object that contains the space needed to store a SPARK record. + * + * \param BYTES size of the SPARK record in bytes + */ + template + struct Spark_object + { + /** + * Exception type + */ + struct Object_size_mismatch { }; + + static constexpr Genode::uint32_t bytes() { return BYTES; } + + long _space[(BYTES + sizeof(long) - 1)/sizeof(long)] { }; + }; + + template + static inline void assert_valid_object_size() + { + if (object_size(*(T *)nullptr) > T::bytes()) { + Genode::error("need ", object_size(*(T *)nullptr), + " bytes, got ", T::bytes(), " bytes"); + throw typename T::Object_size_mismatch(); + } + } + + template + static inline void assert_same_object_size() + { + if (object_size(*(T *)nullptr) != T::bytes()) { + Genode::error("need ", object_size(*(T *)nullptr), + " bytes, got ", T::bytes(), " bytes"); + throw typename T::Object_size_mismatch(); + } + } + +} /* namespace Cbe */ + +#endif /* _CBE_SPARK_OBJECT_H_ */ diff --git a/repos/gems/include/cbe/types.h b/repos/gems/include/cbe/types.h new file mode 100644 index 0000000000..6aef15aba0 --- /dev/null +++ b/repos/gems/include/cbe/types.h @@ -0,0 +1,448 @@ +/* + * \brief Integration of the Consistent Block Encrypter (CBE) + * \author Martin Stein + * \author Josef Soentgen + * \date 2020-11-10 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _CBE_TYPES_H_ +#define _CBE_TYPES_H_ + +/* Genode includes */ +#include +#include +#include +#include + +namespace Cbe { + + using namespace Genode; + using Number_of_primitives = size_t; + using Physical_block_address = uint64_t; + using Virtual_block_address = uint64_t; + using Generation = uint64_t; + using Height = uint32_t; + using Number_of_leaves = uint64_t; + using Number_of_leafs = uint64_t; + using Number_of_blocks = uint64_t; + using Degree = uint32_t; + + static constexpr uint32_t BLOCK_SIZE = 4096; + static constexpr uint32_t NR_OF_SNAPSHOTS = 48; + + + class Request + { + public: + + enum class Operation : uint32_t { + INVALID = 0, + READ = 1, + WRITE = 2, + SYNC = 3, + CREATE_SNAPSHOT = 4, + DISCARD_SNAPSHOT = 5, + REKEY = 6, + EXTEND_VBD = 7, + EXTEND_FT = 8, + RESUME_REKEYING = 10, + DEINITIALIZE = 11, + INITIALIZE = 12, + }; + + private: + + Operation _operation; + bool _success; + uint64_t _block_number; + uint64_t _offset; + Number_of_blocks _count; + uint32_t _key_id; + uint32_t _tag; + + public: + + Request(Operation operation, + bool success, + uint64_t block_number, + uint64_t offset, + Number_of_blocks count, + uint32_t key_id, + uint32_t tag) + : + _operation { operation }, + _success { success }, + _block_number { block_number }, + _offset { offset }, + _count { count }, + _key_id { key_id }, + _tag { tag } + { } + + Request() + : + _operation { Operation::INVALID }, + _success { false }, + _block_number { 0 }, + _offset { 0 }, + _count { 0 }, + _key_id { 0 }, + _tag { 0 } + { } + + bool valid() const + { + return _operation != Operation::INVALID; + } + + void print(Genode::Output &out) const; + + + /*************** + ** Accessors ** + ***************/ + + bool read() const { return _operation == Operation::READ; } + bool write() const { return _operation == Operation::WRITE; } + bool sync() const { return _operation == Operation::SYNC; } + bool create_snapshot() const { return _operation == Operation::CREATE_SNAPSHOT; } + bool discard_snapshot() const { return _operation == Operation::DISCARD_SNAPSHOT; } + bool rekey() const { return _operation == Operation::REKEY; } + bool extend_vbd() const { return _operation == Operation::EXTEND_VBD; } + bool extend_ft() const { return _operation == Operation::EXTEND_FT; } + bool resume_rekeying() const { return _operation == Operation::RESUME_REKEYING; } + bool deinitialize() const { return _operation == Operation::DEINITIALIZE; } + bool initialize() const { return _operation == Operation::INITIALIZE; } + + Operation operation() const { return _operation; } + bool success() const { return _success; } + uint64_t block_number() const { return _block_number; } + uint64_t offset() const { return _offset; } + Number_of_blocks count() const { return _count; } + uint32_t key_id() const { return _key_id; } + uint32_t tag() const { return _tag; } + + void success(bool arg) { _success = arg; } + void tag(uint32_t arg) { _tag = arg; } + + } __attribute__((packed)); + + class Trust_anchor_request + { + public: + + enum class Operation : uint32_t { + INVALID = 0, + CREATE_KEY = 1, + SECURE_SUPERBLOCK = 2, + ENCRYPT_KEY = 3, + DECRYPT_KEY = 4, + LAST_SB_HASH = 5, + INITIALIZE = 6, + }; + + private: + + Operation _operation; + bool _success; + uint32_t _tag; + + public: + + Trust_anchor_request() + : + _operation { Operation::INVALID }, + _success { false }, + _tag { 0 } + { } + + Trust_anchor_request(Operation operation, + bool success, + uint32_t tag) + : + _operation { operation }, + _success { success }, + _tag { tag } + { } + + void print(Genode::Output &out) const; + + bool valid() const { return _operation != Operation::INVALID; } + bool create_key() const { return _operation == Operation::CREATE_KEY; } + bool secure_superblock() const { return _operation == Operation::SECURE_SUPERBLOCK; } + bool encrypt_key() const { return _operation == Operation::ENCRYPT_KEY; } + bool decrypt_key() const { return _operation == Operation::DECRYPT_KEY; } + bool last_sb_hash() const { return _operation == Operation::LAST_SB_HASH; } + bool initialize() const { return _operation == Operation::INITIALIZE; } + + Operation operation() const { return _operation; } + bool success() const { return _success; } + uint32_t tag() const { return _tag; } + + void tag(uint32_t arg) { _tag = arg; } + void success(bool arg) { _success = arg; } + + } __attribute__((packed)); + + + struct Block_data + { + char values[BLOCK_SIZE]; + + void print(Genode::Output &out) const + { + using namespace Genode; + for (char const c : values) { + Genode::print(out, Hex(c, Hex::OMIT_PREFIX, Hex::PAD), " "); + } + Genode::print(out, "\n"); + } + } __attribute__((packed)); + + + class Io_buffer + { + private: + + Block_data items[1]; + + public: + + struct Bad_index : Genode::Exception { }; + + struct Index + { + uint32_t value; + + explicit Index(uint32_t value) : value(value) { } + + } __attribute__((packed)); + + Block_data &item(Index const idx) + { + if (idx.value >= sizeof(items) / sizeof(items[0])) { + throw Bad_index(); + } + return items[idx.value]; + } + + } __attribute__((packed)); + + + class Crypto_plain_buffer + { + private: + + Block_data items[1]; + + public: + + struct Bad_index : Genode::Exception { }; + + struct Index + { + uint32_t value; + + explicit Index(uint32_t value) : value(value) { } + + } __attribute__((packed)); + + Block_data &item(Index const idx) + { + if (idx.value >= sizeof(items) / sizeof(items[0])) { + throw Bad_index(); + } + return items[idx.value]; + } + + } __attribute__((packed)); + + + class Crypto_cipher_buffer + { + private: + + Block_data items[1]; + + public: + + struct Bad_index : Genode::Exception { }; + + struct Index + { + uint32_t value; + + explicit Index(uint32_t value) : value(value) { } + + } __attribute__((packed)); + + Block_data &item(Index const idx) + { + if (idx.value >= sizeof(items) / sizeof(items[0])) { + throw Bad_index(); + } + return items[idx.value]; + } + } __attribute__((packed)); + + + /* + * The Hash contains the hash of a node. + */ + struct Hash + { + enum { MAX_LENGTH = 32, }; + char values[MAX_LENGTH]; + + /* hash as hex value plus "0x" prefix and terminating null */ + using String = Genode::String; + + /* debug */ + void print(Genode::Output &out) const + { + using namespace Genode; + Genode::print(out, "0x"); + bool leading_zero = true; + for (char const c : values) { + if (leading_zero) { + if (c) { + leading_zero = false; + Genode::print(out, Hex(c, Hex::OMIT_PREFIX)); + } + } else { + Genode::print(out, Hex(c, Hex::OMIT_PREFIX, Hex::PAD)); + } + } + if (leading_zero) { + Genode::print(out, "0"); + } + } + }; + + struct Key_plaintext_value + { + enum { KEY_SIZE = 32 }; + char value[KEY_SIZE]; + }; + + struct Key_ciphertext_value + { + enum { KEY_SIZE = 32 }; + char value[KEY_SIZE]; + }; + + /* + * The Key contains the key-material that is used to + * process cipher-blocks. + * + * (For now it is not used but the ID field is already referenced + * by type 2 nodes.) + */ + struct Key + { + enum { KEY_SIZE = 32 }; + char value[KEY_SIZE]; + + struct Id { uint32_t value; }; + Id id; + + using String = Genode::String; + + void print(Genode::Output &out) const + { + using namespace Genode; + Genode::print(out, "[", id.value, ", "); + for (uint32_t i = 0; i < 4; i++) { + Genode::print(out, Hex(value[i], Hex::OMIT_PREFIX, Hex::PAD)); + } + Genode::print(out, "...]"); + } + } __attribute__((packed)); + + + struct Active_snapshot_ids + { + uint64_t values[NR_OF_SNAPSHOTS]; + } __attribute__((packed)); + + + struct Info + { + bool valid; + bool rekeying; + bool extending_vbd; + bool extending_ft; + } __attribute__((packed)); +} + + +inline char const *to_string(Cbe::Request::Operation op) +{ + struct Unknown_operation_type : Genode::Exception { }; + switch (op) { + case Cbe::Request::Operation::INVALID: return "invalid"; + case Cbe::Request::Operation::READ: return "read"; + case Cbe::Request::Operation::WRITE: return "write"; + case Cbe::Request::Operation::SYNC: return "sync"; + case Cbe::Request::Operation::CREATE_SNAPSHOT: return "create_snapshot"; + case Cbe::Request::Operation::DISCARD_SNAPSHOT: return "discard_snapshot"; + case Cbe::Request::Operation::REKEY: return "rekey"; + case Cbe::Request::Operation::EXTEND_VBD: return "extend_vbd"; + case Cbe::Request::Operation::EXTEND_FT: return "extend_ft"; + case Cbe::Request::Operation::RESUME_REKEYING: return "resume_rekeying"; + case Cbe::Request::Operation::DEINITIALIZE: return "deinitialize"; + case Cbe::Request::Operation::INITIALIZE: return "initialize"; + } + throw Unknown_operation_type(); +} + + +inline char const *to_string(Cbe::Trust_anchor_request::Operation op) +{ + struct Unknown_operation_type : Genode::Exception { }; + switch (op) { + case Cbe::Trust_anchor_request::Operation::INVALID: return "invalid"; + case Cbe::Trust_anchor_request::Operation::CREATE_KEY: return "create_key"; + case Cbe::Trust_anchor_request::Operation::INITIALIZE: return "initialize"; + case Cbe::Trust_anchor_request::Operation::SECURE_SUPERBLOCK: return "secure_superblock"; + case Cbe::Trust_anchor_request::Operation::ENCRYPT_KEY: return "encrypt_key"; + case Cbe::Trust_anchor_request::Operation::DECRYPT_KEY: return "decrypt_key"; + case Cbe::Trust_anchor_request::Operation::LAST_SB_HASH: return "last_sb_hash"; + } + throw Unknown_operation_type(); +} + + +inline void Cbe::Request::print(Genode::Output &out) const +{ + if (!valid()) { + Genode::print(out, ""); + return; + } + Genode::print(out, "op=", to_string (_operation)); + Genode::print(out, " vba=", _block_number); + Genode::print(out, " cnt=", _count); + Genode::print(out, " tag=", _tag); + Genode::print(out, " key=", _key_id); + Genode::print(out, " off=", _offset); + Genode::print(out, " succ=", _success); +} + +inline void Cbe::Trust_anchor_request::print(Genode::Output &out) const +{ + if (!valid()) { + Genode::print(out, ""); + return; + } + Genode::print(out, "op=", to_string (_operation)); + Genode::print(out, " tag=", _tag); + Genode::print(out, " succ=", _success); +} +#endif /* _CBE_TYPES_H_ */ diff --git a/repos/gems/include/cbe/vfs/io_job.h b/repos/gems/include/cbe/vfs/io_job.h new file mode 100644 index 0000000000..1000ccc616 --- /dev/null +++ b/repos/gems/include/cbe/vfs/io_job.h @@ -0,0 +1,314 @@ +/* + * \brief Integration of the Consistent Block Encrypter (CBE) + * \author Martin Stein + * \author Josef Soentgen + * \date 2020-11-10 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _CBE__VFS__IO_JOB_H_ +#define _CBE__VFS__IO_JOB_H_ + +/* Genode includes */ +#include +#include + +namespace Util { + + using file_size = Vfs::file_size; + using file_offset = Vfs::file_offset; + + struct Io_job + { + struct Buffer + { + char *base; + file_size size; + }; + + enum class Operation { INVALID, READ, WRITE, SYNC }; + + static char const *to_string(Operation op) + { + using Op = Operation; + + switch (op) { + case Op::READ: return "READ"; + case Op::WRITE: return "WRITE"; + case Op::SYNC: return "SYNC"; + default: return "INVALID"; + } + } + + struct Unsupported_Operation : Genode::Exception { }; + struct Invalid_state : Genode::Exception { }; + + enum State { PENDING, IN_PROGRESS, COMPLETE, }; + + static State _initial_state(Operation const op) + { + using Op = Operation; + + switch (op) { + case Op::READ: return State::PENDING; + case Op::WRITE: return State::PENDING; + case Op::SYNC: return State::PENDING; + default: throw Unsupported_Operation(); + } + } + + static char const *_state_to_string(State s) + { + switch (s) { + case State::PENDING: return "PENDING"; + case State::IN_PROGRESS: return "IN_PROGRESS"; + case State::COMPLETE: return "COMPLETE"; + } + + throw Invalid_state(); + } + + enum class Partial_result { ALLOW, DENY }; + + Vfs::Vfs_handle &_handle; + + Operation const _op; + State _state; + char *_data; + file_offset const _base_offset; + file_offset _current_offset; + file_size _current_count; + + bool const _allow_partial; + + bool _success; + bool _complete; + + bool _read() + { + bool progress = false; + + switch (_state) { + case State::PENDING: + + _handle.seek(_base_offset + _current_offset); + if (!_handle.fs().queue_read(&_handle, _current_count)) { + return progress; + } + + _state = State::IN_PROGRESS; + progress = true; + [[fallthrough]]; + case State::IN_PROGRESS: + { + using Result = Vfs::File_io_service::Read_result; + + bool completed = false; + file_size out = 0; + + Result const result = + _handle.fs().complete_read(&_handle, + _data + _current_offset, + _current_count, out); + if ( result == Result::READ_QUEUED + || result == Result::READ_ERR_INTERRUPT + || result == Result::READ_ERR_AGAIN + || result == Result::READ_ERR_WOULD_BLOCK) { + return progress; + } else + + if (result == Result::READ_OK) { + _current_offset += out; + _current_count -= out; + _success = true; + } else + + if ( result == Result::READ_ERR_IO + || result == Result::READ_ERR_INVALID) { + _success = false; + completed = true; + } + + if (_current_count == 0 || completed || (out == 0 && _allow_partial)) { + _state = State::COMPLETE; + } else { + _state = State::PENDING; + /* partial read, keep trying */ + return true; + } + progress = true; + } + [[fallthrough]]; + case State::COMPLETE: + + _complete = true; + progress = true; + default: break; + } + + return progress; + } + + bool _write() + { + bool progress = false; + + switch (_state) { + case State::PENDING: + + _handle.seek(_base_offset + _current_offset); + + _state = State::IN_PROGRESS; + progress = true; + [[fallthrough]]; + case State::IN_PROGRESS: + { + using Result = Vfs::File_io_service::Write_result; + + bool completed = false; + file_size out = 0; + + Result result = Result::WRITE_ERR_INVALID; + try { + result = _handle.fs().write(&_handle, + _data + _current_offset, + _current_count, out); + } catch (Vfs::File_io_service::Insufficient_buffer) { + return progress; + } + + if ( result == Result::WRITE_ERR_AGAIN + || result == Result::WRITE_ERR_INTERRUPT + || result == Result::WRITE_ERR_WOULD_BLOCK) { + return progress; + } else + + if (result == Result::WRITE_OK) { + _current_offset += out; + _current_count -= out; + _success = true; + } else + + if ( result == Result::WRITE_ERR_IO + || result == Result::WRITE_ERR_INVALID) { + _success = false; + completed = true; + } + + if (_current_count == 0 || completed || (out == 0 && _allow_partial)) { + _state = State::COMPLETE; + } else { + _state = State::PENDING; + /* partial write, keep trying */ + return true; + } + progress = true; + } + [[fallthrough]]; + case State::COMPLETE: + + _complete = true; + progress = true; + default: break; + } + + return progress; + } + + bool _sync() + { + bool progress = false; + + switch (_state) { + case State::PENDING: + + if (!_handle.fs().queue_sync(&_handle)) { + return progress; + } + _state = State::IN_PROGRESS; + progress = true; + [[fallthrough]]; + case State::IN_PROGRESS: + { + using Result = Vfs::File_io_service::Sync_result; + Result const result = _handle.fs().complete_sync(&_handle); + + if (result == Result::SYNC_QUEUED) { + return progress; + } else + + if (result == Result::SYNC_ERR_INVALID) { + _success = false; + } else + + if (result == Result::SYNC_OK) { + _success = true; + } + + _state = State::COMPLETE; + progress = true; + } + [[fallthrough]]; + case State::COMPLETE: + + _complete = true; + progress = true; + default: break; + } + + return progress; + } + + Io_job(Vfs::Vfs_handle &handle, + Operation op, + Buffer &buffer, + file_offset base_offset, + Partial_result partial_result = Partial_result::DENY) + : + _handle { handle }, + _op { op }, + _state { _initial_state(op) }, + _data { buffer.base }, + _base_offset { base_offset }, + _current_offset { 0 }, + _current_count { buffer.size }, + _allow_partial { partial_result == Partial_result::ALLOW }, + _success { false }, + _complete { false } + { } + + bool completed() const { return _complete; } + bool succeeded() const { return _success; } + + void print(Genode::Output &out) const + { + Genode::print(out, "(", to_string(_op), ")", + " state: ", _state_to_string(_state), + " current_offset: ", _current_offset, + " current_count: ", _current_count, + " success: ", _success, + " complete: ", _complete); + } + + bool execute() + { + using Op = Operation; + + switch (_op) { + case Op::READ: return _read(); + case Op::WRITE: return _write(); + case Op::SYNC: return _sync(); + default: return false; + } + } + }; + +} /* namespace Util */ + +#endif /* _CBE__VFS__IO_JOB_H_ */ diff --git a/repos/gems/include/cbe/vfs/trust_anchor_vfs.h b/repos/gems/include/cbe/vfs/trust_anchor_vfs.h new file mode 100644 index 0000000000..91d6efa4f1 --- /dev/null +++ b/repos/gems/include/cbe/vfs/trust_anchor_vfs.h @@ -0,0 +1,761 @@ +/* + * \brief Integration of the Consistent Block Encrypter (CBE) + * \author Martin Stein + * \author Josef Soentgen + * \date 2020-11-10 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _CBE__VFS__TRUST_ANCHOR_VFS_H_ +#define _CBE__VFS__TRUST_ANCHOR_VFS_H_ + +/* local includes */ +#include + +namespace Util { + + using namespace Genode; + + struct Trust_anchor_vfs; + +}; /* namespace Util */ + + +struct Util::Trust_anchor_vfs +{ + struct Invalid_request : Genode::Exception { }; + + using Path = Genode::Path<256>; + + Vfs::File_system &_vfs; + + struct Io_response_handler : Vfs::Io_response_handler + { + Genode::Signal_context_capability _io_sigh; + + Io_response_handler(Genode::Signal_context_capability io_sigh) + : _io_sigh(io_sigh) { } + + void read_ready_response() override { } + + void io_progress_response() override + { + if (_io_sigh.valid()) { + Genode::Signal_transmitter(_io_sigh).submit(); + } + } + }; + + Io_response_handler _io_response_handler; + + struct File + { + struct Could_not_open_file : Genode::Exception { }; + struct Io_job_already_constructed : Genode::Exception { }; + struct Cannot_drop_unconstructed_io_job : Genode::Exception { }; + + struct Completed_io_job + { + bool completed; + bool success; + }; + + File(File const &) = delete; + File &operator=(File const&) = delete; + + Vfs::File_system &_vfs; + Vfs::Vfs_handle *_vfs_handle; + + Genode::Constructible _io_job { }; + + File(Path const &base_path, + char const *name, + Vfs::File_system &vfs, + Genode::Allocator &alloc, + Io_response_handler &io_response_handler) + : + _vfs { vfs }, + _vfs_handle { nullptr } + { + using Result = Vfs::Directory_service::Open_result; + + Path file_path = base_path; + file_path.append_element(name); + + Result const res = + _vfs.open(file_path.string(), + Vfs::Directory_service::OPEN_MODE_RDWR, + (Vfs::Vfs_handle **)&_vfs_handle, alloc); + if (res != Result::OPEN_OK) { + error("could not open '", file_path.string(), "'"); + throw Could_not_open_file(); + } + + _vfs_handle->handler(&io_response_handler); + } + + ~File() + { + _vfs.close(_vfs_handle); + } + + bool submit_io_job(Util::Io_job::Buffer &buffer, + Util::Io_job::Operation op) + { + if (_io_job.constructed()) { + // throw Io_job_already_constructed(); + return false; + } + + _io_job.construct(*_vfs_handle, op, buffer, 0); + return true; + } + + bool execute_io_job() + { + if (!_io_job.constructed()) { + return false; + } + return _io_job->execute(); + } + + void drop_io_job() + { + if (!_io_job.constructed()) { + throw Cannot_drop_unconstructed_io_job(); + } + _io_job.destruct(); + } + + Completed_io_job completed_io_job() + { + if (!_io_job.constructed()) { + return { false, false }; + } + + return { _io_job->completed(), _io_job->succeeded() }; + } + }; + + Util::Io_job::Buffer _init_io_buffer { }; + Genode::Constructible _init_file { }; + + Util::Io_job::Buffer _encrypt_io_buffer { }; + Genode::Constructible _encrypt_file { }; + + Util::Io_job::Buffer _decrypt_io_buffer { }; + Genode::Constructible _decrypt_file { }; + + Util::Io_job::Buffer _generate_key_io_buffer { }; + Genode::Constructible _generate_key_file { }; + + Util::Io_job::Buffer _last_hash_io_buffer { }; + Genode::Constructible _last_hash_file { }; + + Path _ta_dir { }; + + struct Job + { + enum class Type { + NONE, + DECRYPT_WRITE, + DECRYPT_READ, + ENCRYPT_WRITE, + ENCRYPT_READ, + GENERATE, + INIT_WRITE, + INIT_READ, + HASH_READ, + HASH_UPDATE_WRITE, + HASH_UPDATE_READ, + }; + Type type { Type::NONE }; + + static char const *to_string(Type const type) + { + switch (type) { + case Type::NONE: return "NONE"; + case Type::DECRYPT_WRITE: return "DECRYPT_WRITE"; + case Type::DECRYPT_READ: return "DECRYPT_READ"; + case Type::ENCRYPT_WRITE: return "ENCRYPT_WRITE"; + case Type::ENCRYPT_READ: return "ENCRYPT_READ"; + case Type::GENERATE: return "GENERATE"; + case Type::INIT_WRITE: return "INIT_WRITE"; + case Type::INIT_READ: return "INIT_READ"; + case Type::HASH_READ: return "HASH_READ"; + case Type::HASH_UPDATE_WRITE: return "HASH_UPDATE_WRITE"; + case Type::HASH_UPDATE_READ: return "HASH_UPDATE_READ"; + } + return nullptr; + } + + enum class State { + NONE, + PENDING, + IN_PROGRESS, + COMPLETE + }; + State state { State::NONE }; + + static char const *to_string(State const state) + { + switch (state) { + case State::NONE: return "NONE"; + case State::PENDING: return "PENDING"; + case State::IN_PROGRESS: return "IN_PROGRESS"; + case State::COMPLETE: return "COMPLETE"; + } + return nullptr; + } + + Cbe::Hash hash; + Cbe::Key_plaintext_value plain; + Cbe::Key_ciphertext_value cipher; + + Cbe::Trust_anchor_request request { }; + bool success { false }; + + void reset() + { + type = Type::NONE; + state = State::NONE; + request = Cbe::Trust_anchor_request(); + } + + bool valid() const { return state != State::NONE; } + + bool completed() const { return state == State::COMPLETE; } + + bool equals(Cbe::Trust_anchor_request const &other) const + { + return request.operation() == other.operation() + && request.tag() == other.tag(); + } + + void print(Genode::Output &out) const + { + if (!valid()) { + Genode::print(out, ""); + return; + } + + Genode::print(out, "type: ", to_string(type)); + Genode::print(out, " state: ", to_string(state)); + Genode::print(out, " request: ", request); + } + }; + + Job _job { }; + + bool _execute_decrypt(Job &job, bool write) + { + bool progress = false; + File::Completed_io_job completed_io_job { false, false }; + + switch (job.state) { + case Job::State::PENDING: + { + using Op = Util::Io_job::Operation; + + Op const op = write ? Op::WRITE : Op::READ; + if (!_decrypt_file->submit_io_job(_decrypt_io_buffer, op)) { + break; + } + job.state = Job::State::IN_PROGRESS; + progress |= true; + } + [[fallthrough]]; + case Job::State::IN_PROGRESS: + if (!_decrypt_file->execute_io_job()) { + break; + } + + progress |= true; + + completed_io_job = _decrypt_file->completed_io_job(); + if (!completed_io_job.completed) { + break; + } + _decrypt_file->drop_io_job(); + + /* setup second phase */ + if (write) { + + /* + * In case the write request was not successful it + * is not needed to try to read the result. + */ + if (!completed_io_job.success) { + job.state = Job::State::COMPLETE; + job.request.success(false); + break; + } + + _decrypt_io_buffer = { + .base = _job.cipher.value, + .size = sizeof (_job.cipher) + }; + + job.type = Job::Type::DECRYPT_READ; + job.state = Job::State::PENDING; + break; + } + + job.state = Job::State::COMPLETE; + job.success = completed_io_job.success; + job.request.success(job.success); + [[fallthrough]]; + case Job::State::COMPLETE: break; + case Job::State::NONE: break; + } + return progress; + } + + bool _execute_encrypt(Job &job, bool write) + { + bool progress = false; + File::Completed_io_job completed_io_job { false, false }; + + switch (job.state) { + case Job::State::PENDING: + { + using Op = Util::Io_job::Operation; + + Op const op = write ? Op::WRITE : Op::READ; + if (!_encrypt_file->submit_io_job(_encrypt_io_buffer, op)) { + break; + } + job.state = Job::State::IN_PROGRESS; + progress |= true; + } + [[fallthrough]]; + case Job::State::IN_PROGRESS: + if (!_encrypt_file->execute_io_job()) { + break; + } + + progress |= true; + + completed_io_job = _encrypt_file->completed_io_job(); + if (!completed_io_job.completed) { + break; + } + _encrypt_file->drop_io_job(); + + /* setup second phase */ + if (write) { + + /* + * In case the write request was not successful it + * is not needed to try to read the result. + */ + if (!completed_io_job.success) { + job.state = Job::State::COMPLETE; + job.request.success(false); + break; + } + + _encrypt_io_buffer = { + .base = _job.cipher.value, + .size = sizeof (_job.cipher) + }; + + job.type = Job::Type::ENCRYPT_READ; + job.state = Job::State::PENDING; + break; + } + + job.state = Job::State::COMPLETE; + job.success = completed_io_job.success; + job.request.success(job.success); + [[fallthrough]]; + case Job::State::COMPLETE: break; + case Job::State::NONE: break; + } + return progress; + } + + bool _execute_generate(Job &job) + { + bool progress = false; + File::Completed_io_job completed_io_job { false, false }; + + switch (job.state) { + case Job::State::PENDING: + if (!_generate_key_file->submit_io_job(_generate_key_io_buffer, + Util::Io_job::Operation::READ)) { + break; + } + job.state = Job::State::IN_PROGRESS; + progress |= true; + [[fallthrough]]; + case Job::State::IN_PROGRESS: + if (!_generate_key_file->execute_io_job()) { + break; + } + + progress |= true; + + completed_io_job = _generate_key_file->completed_io_job(); + if (!completed_io_job.completed) { + break; + } + _generate_key_file->drop_io_job(); + + job.state = Job::State::COMPLETE; + job.success = completed_io_job.success; + job.request.success(job.success); + [[fallthrough]]; + case Job::State::COMPLETE: break; + case Job::State::NONE: break; + } + return progress; + } + + bool _execute_init(Job &job, bool write) + { + bool progress = false; + File::Completed_io_job completed_io_job { false, false }; + + switch (job.state) { + case Job::State::PENDING: + { + using Op = Util::Io_job::Operation; + + Op const op = write ? Op::WRITE : Op::READ; + if (!_init_file->submit_io_job(_init_io_buffer, op)) { + break; + } + job.state = Job::State::IN_PROGRESS; + progress |= true; + } + [[fallthrough]]; + case Job::State::IN_PROGRESS: + if (!_init_file->execute_io_job()) { + break; + } + + progress |= true; + + completed_io_job = _init_file->completed_io_job(); + if (!completed_io_job.completed) { + break; + } + _init_file->drop_io_job(); + + /* setup second phase */ + if (write) { + + /* + * In case the write request was not successful it + * is not needed to try to read the result. + */ + if (!completed_io_job.success) { + job.state = Job::State::COMPLETE; + job.request.success(false); + break; + } + + _init_io_buffer = { + .base = _job.cipher.value, + .size = sizeof (_job.cipher) + }; + + job.type = Job::Type::INIT_READ; + job.state = Job::State::PENDING; + break; + } + + job.state = Job::State::COMPLETE; + job.success = completed_io_job.success; + job.request.success(job.success); + [[fallthrough]]; + case Job::State::COMPLETE: break; + case Job::State::NONE: break; + } + return progress; + } + + bool _execute_read_hash(Job &job) + { + bool progress = false; + File::Completed_io_job completed_io_job { false, false }; + + switch (job.state) { + case Job::State::PENDING: + if (!_last_hash_file->submit_io_job(_last_hash_io_buffer, + Util::Io_job::Operation::READ)) { + break; + } + job.state = Job::State::IN_PROGRESS; + progress |= true; + [[fallthrough]]; + case Job::State::IN_PROGRESS: + if (!_last_hash_file->execute_io_job()) { + break; + } + + progress |= true; + + completed_io_job = _last_hash_file->completed_io_job(); + if (!completed_io_job.completed) { + break; + } + _last_hash_file->drop_io_job(); + + job.state = Job::State::COMPLETE; + job.success = completed_io_job.success; + job.request.success(job.success); + [[fallthrough]]; + case Job::State::COMPLETE: break; + case Job::State::NONE: break; + } + return progress; + } + + bool _execute_update_hash(Job &job, bool write) + { + bool progress = false; + File::Completed_io_job completed_io_job { false, false }; + + switch (job.state) { + case Job::State::PENDING: + { + using Op = Util::Io_job::Operation; + + Op const op = write ? Op::WRITE : Op::READ; + if (!_last_hash_file->submit_io_job(_last_hash_io_buffer, op)) { + break; + } + job.state = Job::State::IN_PROGRESS; + progress |= true; + } + [[fallthrough]]; + case Job::State::IN_PROGRESS: + if (!_last_hash_file->execute_io_job()) { + break; + } + + progress |= true; + + completed_io_job = _last_hash_file->completed_io_job(); + if (!completed_io_job.completed) { + break; + } + _last_hash_file->drop_io_job(); + + /* setup second phase */ + if (write) { + + /* + * In case the write request was not successful it + * is not needed to try to read the result. + */ + if (!completed_io_job.success) { + job.state = Job::State::COMPLETE; + job.request.success(false); + break; + } + + _last_hash_io_buffer = { + .base = _job.hash.values, + .size = sizeof (_job.hash) + }; + + job.type = Job::Type::HASH_UPDATE_READ; + job.state = Job::State::PENDING; + break; + } + + job.state = Job::State::COMPLETE; + job.success = completed_io_job.success; + job.request.success(job.success); + [[fallthrough]]; + case Job::State::COMPLETE: break; + case Job::State::NONE: break; + } + return progress; + } + + Trust_anchor_vfs(Vfs::File_system &vfs, + Genode::Allocator &alloc, + Path const &path, + Genode::Signal_context_capability io_sigh) + : + _vfs { vfs }, + _io_response_handler { io_sigh }, + _ta_dir { path } + { + _init_file.construct(path, "initialize", _vfs, alloc, _io_response_handler); + _encrypt_file.construct(path, "encrypt", _vfs, alloc, _io_response_handler); + _decrypt_file.construct(path, "decrypt", _vfs, alloc, _io_response_handler); + _generate_key_file.construct(path, "generate_key", _vfs, alloc, _io_response_handler); + _last_hash_file.construct(path, "hashsum", _vfs, alloc, _io_response_handler); + } + + bool request_acceptable() const + { + return !_job.valid(); + } + + void submit_create_key_request(Cbe::Trust_anchor_request const &request) + { + _job = { + .type = Job::Type::GENERATE, + .state = Job::State::PENDING, + .hash = Cbe::Hash(), + .plain = Cbe::Key_plaintext_value(), + .cipher = Cbe::Key_ciphertext_value(), + .request = request, + .success = false, + }; + + _generate_key_io_buffer = { + .base = _job.plain.value, + .size = sizeof (_job.plain) + }; + } + + void submit_superblock_hash_request(Cbe::Trust_anchor_request const &request) + { + _job = { + .type = Job::Type::HASH_READ, + .state = Job::State::PENDING, + .hash = Cbe::Hash(), + .plain = Cbe::Key_plaintext_value(), + .cipher = Cbe::Key_ciphertext_value(), + .request = request, + .success = false, + }; + + _last_hash_io_buffer = { + .base = _job.hash.values, + .size = sizeof (_job.hash) + }; + } + + void submit_secure_superblock_request(Cbe::Trust_anchor_request const &request, + Cbe::Hash const &hash) + { + _job = { + .type = Job::Type::HASH_UPDATE_WRITE, + .state = Job::State::PENDING, + .hash = hash, + .plain = Cbe::Key_plaintext_value(), + .cipher = Cbe::Key_ciphertext_value(), + .request = request, + .success = false, + }; + + _last_hash_io_buffer = { + .base = _job.hash.values, + .size = sizeof (_job.hash) + }; + } + + void submit_encrypt_key_request(Cbe::Trust_anchor_request const &request, + Cbe::Key_plaintext_value const &plain) + { + _job = { + .type = Job::Type::ENCRYPT_WRITE, + .state = Job::State::PENDING, + .hash = Cbe::Hash(), + .plain = plain, + .cipher = Cbe::Key_ciphertext_value(), + .request = request, + .success = false, + }; + + _encrypt_io_buffer = { + .base = _job.plain.value, + .size = sizeof (_job.plain) + }; + } + + void submit_decrypt_key_request(Cbe::Trust_anchor_request const &request, + Cbe::Key_ciphertext_value const &cipher) + { + _job = { + .type = Job::Type::DECRYPT_WRITE, + .state = Job::State::PENDING, + .hash = Cbe::Hash(), + .plain = Cbe::Key_plaintext_value(), + .cipher = cipher, + .request = request, + .success = false, + }; + + _decrypt_io_buffer = { + .base = _job.cipher.value, + .size = sizeof (_job.cipher) + }; + } + + Cbe::Trust_anchor_request peek_completed_request() + { + return _job.completed() ? _job.request : Cbe::Trust_anchor_request(); + } + + void drop_completed_request(Cbe::Trust_anchor_request const &request) + { + if (!_job.equals(request)) { + throw Invalid_request(); + } + + _job.reset(); + } + + Cbe::Hash peek_completed_superblock_hash(Cbe::Trust_anchor_request const &request) + { + if (!_job.equals(request) || !_job.completed()) { + throw Invalid_request(); + } + + return _job.hash; + } + + Cbe::Key_plaintext_value peek_completed_key_value_plaintext(Cbe::Trust_anchor_request const &request) + { + if (!_job.equals(request) || !_job.completed()) { + throw Invalid_request(); + } + + return _job.plain; + } + + Cbe::Key_ciphertext_value peek_completed_key_value_ciphertext(Cbe::Trust_anchor_request const &request) + { + if (!_job.equals(request) || !_job.completed()) { + throw Invalid_request(); + } + return _job.cipher; + } + + bool execute() + { + bool progress = false; + + switch (_job.type) { + case Job::Type::NONE: break; + case Job::Type::DECRYPT_WRITE: progress |= _execute_decrypt(_job, true); break; + case Job::Type::DECRYPT_READ: progress |= _execute_decrypt(_job, false); break; + case Job::Type::ENCRYPT_WRITE: progress |= _execute_encrypt(_job, true); break; + case Job::Type::ENCRYPT_READ: progress |= _execute_encrypt(_job, false); break; + case Job::Type::GENERATE: progress |= _execute_generate(_job); break; + case Job::Type::INIT_WRITE: progress |= _execute_init(_job, true); break; + case Job::Type::INIT_READ: progress |= _execute_init(_job, false); break; + case Job::Type::HASH_READ: progress |= _execute_read_hash(_job); break; + case Job::Type::HASH_UPDATE_WRITE: progress |= _execute_update_hash(_job, true); break; + case Job::Type::HASH_UPDATE_READ: progress |= _execute_update_hash(_job, false); break; + } + return progress; + } +}; + +#endif /* _CBE__VFS__TRUST_ANCHOR_VFS_H_ */ diff --git a/repos/gems/lib/import/import-cbe.mk b/repos/gems/lib/import/import-cbe.mk new file mode 100644 index 0000000000..51b1019e20 --- /dev/null +++ b/repos/gems/lib/import/import-cbe.mk @@ -0,0 +1,3 @@ +CBE_DIR = $(call select_from_ports,cbe) + +INC_DIR += $(CBE_DIR)/src/lib/cbe diff --git a/repos/gems/lib/import/import-cbe_check.mk b/repos/gems/lib/import/import-cbe_check.mk new file mode 100644 index 0000000000..63b108607d --- /dev/null +++ b/repos/gems/lib/import/import-cbe_check.mk @@ -0,0 +1,2 @@ + +INC_DIR += $(call select_from_repositories,src/lib/cbe_check) diff --git a/repos/gems/lib/import/import-cbe_common.mk b/repos/gems/lib/import/import-cbe_common.mk new file mode 100644 index 0000000000..439996f1f3 --- /dev/null +++ b/repos/gems/lib/import/import-cbe_common.mk @@ -0,0 +1,3 @@ +CBE_DIR = $(call select_from_ports,cbe) + +INC_DIR += $(CBE_DIR)/src/lib/cbe_common diff --git a/repos/gems/lib/import/import-cbe_dump.mk b/repos/gems/lib/import/import-cbe_dump.mk new file mode 100644 index 0000000000..7bfdc02eb7 --- /dev/null +++ b/repos/gems/lib/import/import-cbe_dump.mk @@ -0,0 +1,2 @@ + +INC_DIR += $(call select_from_repositories,src/lib/cbe_dump) diff --git a/repos/gems/lib/import/import-cbe_init.mk b/repos/gems/lib/import/import-cbe_init.mk new file mode 100644 index 0000000000..5c2e60c212 --- /dev/null +++ b/repos/gems/lib/import/import-cbe_init.mk @@ -0,0 +1,2 @@ + +INC_DIR += $(call select_from_repositories,src/lib/cbe_init) diff --git a/repos/gems/lib/import/import-sha256_4k.mk b/repos/gems/lib/import/import-sha256_4k.mk new file mode 100644 index 0000000000..827297e31e --- /dev/null +++ b/repos/gems/lib/import/import-sha256_4k.mk @@ -0,0 +1,3 @@ +CBE_DIR = $(call select_from_ports,cbe) + +INC_DIR += $(CBE_DIR)/src/lib/sha256_4k diff --git a/repos/gems/lib/mk/generate_ada_main_pkg.inc b/repos/gems/lib/mk/generate_ada_main_pkg.inc new file mode 100644 index 0000000000..e84602fb33 --- /dev/null +++ b/repos/gems/lib/mk/generate_ada_main_pkg.inc @@ -0,0 +1,20 @@ +ifeq ($(called_from_lib_mk),yes) + +CUSTOM_BINDER_FLAGS ?= -n -we -D768k + +ALIS := $(addsuffix .ali, $(basename $(SRC_ADS) $(SRC_ADB))) +ALI_DIRS := $(foreach LIB,$(LIBS),$(call select_from_repositories,lib/ali/$(LIB))) +BINDER_SEARCH_DIRS = $(addprefix -I$(BUILD_BASE_DIR)/var/libcache/, $(LIBS)) $(addprefix -aO, $(ALI_DIRS)) + +# declare after(!) $(ALIS) was evaluated +SRC_ADB += b~$(LIB).adb + +BINDER_SRC := b~$(LIB).adb + +$(BINDER_SRC): $(ALIS) + $(VERBOSE)$(GNATBIND) $(CUSTOM_BINDER_FLAGS) $(BINDER_SEARCH_DIRS) \ + $(INCLUDES) --RTS=$(ADA_RTS) -L$(LIB)_ -o $@ $^ + +all: $(BINDER_SRC) + +endif diff --git a/repos/gems/lib/mk/sha256_4k.mk b/repos/gems/lib/mk/sha256_4k.mk new file mode 100644 index 0000000000..9d84472103 --- /dev/null +++ b/repos/gems/lib/mk/sha256_4k.mk @@ -0,0 +1,12 @@ +CBE_DIR = $(call select_from_ports,cbe) + +SRC_ADB := sha256_4k.adb +LIBS += spark libsparkcrypto + +CC_ADA_OPT += -gnatec=$(CBE_DIR)/src/lib/sha256_4k/spark.adc + +INC_DIR += $(CBE_DIR)/src/lib/sha256_4k + +sha256_4k.o : $(CBE_DIR)/src/lib/sha256_4k/sha256_4k.ads + +vpath % $(CBE_DIR)/src/lib/sha256_4k diff --git a/repos/gems/lib/mk/spec/x86_64/cbe.mk b/repos/gems/lib/mk/spec/x86_64/cbe.mk new file mode 100644 index 0000000000..9a3e6c1333 --- /dev/null +++ b/repos/gems/lib/mk/spec/x86_64/cbe.mk @@ -0,0 +1,23 @@ +CBE_DIR = $(call select_from_ports,cbe) + +LIBS += spark sha256_4k cbe_common + +INC_DIR += $(CBE_DIR)/src/lib/cbe + +SRC_ADB += cbe-library.adb +SRC_ADB += cbe-request_pool.adb +SRC_ADB += cbe-crypto.adb +SRC_ADB += cbe-tree_helper.adb +SRC_ADB += cbe-translation.adb +SRC_ADB += cbe-cache.adb +SRC_ADB += cbe-new_free_tree.adb +SRC_ADB += cbe-ft_resizing.adb +SRC_ADB += cbe-mt_resizing.adb +SRC_ADB += cbe-meta_tree.adb +SRC_ADB += cbe-generic_index_queue.adb +SRC_ADB += cbe-superblock_control.adb +SRC_ADB += cbe-vbd_rekeying.adb + +vpath % $(CBE_DIR)/src/lib/cbe + +CC_ADA_OPT += -gnatec=$(CBE_DIR)/src/lib/cbe/pragmas.adc diff --git a/repos/gems/lib/mk/spec/x86_64/cbe_check.mk b/repos/gems/lib/mk/spec/x86_64/cbe_check.mk new file mode 100644 index 0000000000..3706843c2e --- /dev/null +++ b/repos/gems/lib/mk/spec/x86_64/cbe_check.mk @@ -0,0 +1,14 @@ +CBE_DIR = $(call select_from_ports,cbe) + +LIBS += spark sha256_4k cbe_common + +INC_DIR += $(CBE_DIR)/src/lib/cbe_check + +SRC_ADB += cbe-check_library.adb +SRC_ADB += cbe-superblock_check.adb +SRC_ADB += cbe-vbd_check.adb +SRC_ADB += cbe-free_tree_check.adb + +vpath % $(CBE_DIR)/src/lib/cbe_check + +CC_ADA_OPT += -gnatec=$(CBE_DIR)/src/lib/cbe_check/pragmas.adc diff --git a/repos/gems/lib/mk/spec/x86_64/cbe_check_cxx.mk b/repos/gems/lib/mk/spec/x86_64/cbe_check_cxx.mk new file mode 100644 index 0000000000..a55143eec0 --- /dev/null +++ b/repos/gems/lib/mk/spec/x86_64/cbe_check_cxx.mk @@ -0,0 +1,17 @@ +CBE_DIR = $(call select_from_ports,cbe) + +LIBS += spark libsparkcrypto sha256_4k cbe_common cbe_cxx_common +LIBS += cbe_check + +INC_DIR += $(CBE_DIR)/src/lib/cbe_check +INC_DIR += $(CBE_DIR)/src/lib/cbe_common +INC_DIR += $(CBE_DIR)/src/lib/cbe_check_cxx +INC_DIR += $(CBE_DIR)/src/lib/cbe_cxx_common + +SRC_ADB += cbe-cxx-cxx_check_library.adb + +vpath % $(CBE_DIR)/src/lib/cbe_check_cxx + +SHARED_LIB := yes + +include $(REP_DIR)/lib/mk/generate_ada_main_pkg.inc diff --git a/repos/gems/lib/mk/spec/x86_64/cbe_common.mk b/repos/gems/lib/mk/spec/x86_64/cbe_common.mk new file mode 100644 index 0000000000..f18f46a92b --- /dev/null +++ b/repos/gems/lib/mk/spec/x86_64/cbe_common.mk @@ -0,0 +1,17 @@ +CBE_DIR = $(call select_from_ports,cbe) + +LIBS += spark sha256_4k + +INC_DIR += $(CBE_DIR)/src/lib/cbe_common + +SRC_ADB += cbe.adb +SRC_ADB += cbe-debug.adb +SRC_ADB += cbe-request.adb +SRC_ADB += cbe-primitive.adb +SRC_ADB += cbe-block_io.adb +SRC_ADB += cbe-ta_request.adb +SRC_ADB += cbe-trust_anchor.adb + +vpath % $(CBE_DIR)/src/lib/cbe_common + +CC_ADA_OPT += -gnatec=$(CBE_DIR)/src/lib/cbe_common/pragmas.adc diff --git a/repos/gems/lib/mk/spec/x86_64/cbe_cxx.mk b/repos/gems/lib/mk/spec/x86_64/cbe_cxx.mk new file mode 100644 index 0000000000..9e5cec3260 --- /dev/null +++ b/repos/gems/lib/mk/spec/x86_64/cbe_cxx.mk @@ -0,0 +1,16 @@ +CBE_DIR = $(call select_from_ports,cbe) + +LIBS += spark libsparkcrypto sha256_4k cbe cbe_common cbe_cxx_common + +INC_DIR += $(CBE_DIR)/src/lib/cbe +INC_DIR += $(CBE_DIR)/src/lib/cbe_common +INC_DIR += $(CBE_DIR)/src/lib/cbe_cxx +INC_DIR += $(CBE_DIR)/src/lib/cbe_cxx_common + +SRC_ADB += cbe-cxx-cxx_library.adb + +vpath % $(CBE_DIR)/src/lib/cbe_cxx + +SHARED_LIB := yes + +include $(REP_DIR)/lib/mk/generate_ada_main_pkg.inc diff --git a/repos/gems/lib/mk/spec/x86_64/cbe_cxx_common.mk b/repos/gems/lib/mk/spec/x86_64/cbe_cxx_common.mk new file mode 100644 index 0000000000..2d5fce837b --- /dev/null +++ b/repos/gems/lib/mk/spec/x86_64/cbe_cxx_common.mk @@ -0,0 +1,12 @@ +CBE_DIR = $(call select_from_ports,cbe) + +LIBS += spark cbe_common + +INC_DIR += $(CBE_DIR)/src/lib/cbe_common +INC_DIR += $(CBE_DIR)/src/lib/cbe_cxx_common + +SRC_ADB += cbe-cxx.adb + +SRC_CC += print_cstring.cc + +vpath % $(CBE_DIR)/src/lib/cbe_cxx_common diff --git a/repos/gems/lib/mk/spec/x86_64/cbe_dump.mk b/repos/gems/lib/mk/spec/x86_64/cbe_dump.mk new file mode 100644 index 0000000000..bbf4ad78ec --- /dev/null +++ b/repos/gems/lib/mk/spec/x86_64/cbe_dump.mk @@ -0,0 +1,14 @@ +CBE_DIR = $(call select_from_ports,cbe) + +LIBS += spark sha256_4k cbe_common + +INC_DIR += $(CBE_DIR)/src/lib/cbe_dump + +SRC_ADB += cbe-dump_library.adb +SRC_ADB += cbe-superblock_dump.adb +SRC_ADB += cbe-vbd_dump.adb +SRC_ADB += cbe-free_tree_dump.adb + +vpath % $(CBE_DIR)/src/lib/cbe_dump + +CC_ADA_OPT += -gnatec=$(CBE_DIR)/src/lib/cbe_dump/pragmas.adc diff --git a/repos/gems/lib/mk/spec/x86_64/cbe_dump_cxx.mk b/repos/gems/lib/mk/spec/x86_64/cbe_dump_cxx.mk new file mode 100644 index 0000000000..8894f2fbb1 --- /dev/null +++ b/repos/gems/lib/mk/spec/x86_64/cbe_dump_cxx.mk @@ -0,0 +1,17 @@ +CBE_DIR = $(call select_from_ports,cbe) + +LIBS += spark libsparkcrypto sha256_4k cbe_common cbe_cxx_common +LIBS += cbe_dump + +INC_DIR += $(CBE_DIR)/src/lib/cbe_dump +INC_DIR += $(CBE_DIR)/src/lib/cbe_common +INC_DIR += $(CBE_DIR)/src/lib/cbe_dump_cxx +INC_DIR += $(CBE_DIR)/src/lib/cbe_cxx_common + +SRC_ADB += cbe-cxx-cxx_dump_library.adb + +vpath % $(CBE_DIR)/src/lib/cbe_dump_cxx + +SHARED_LIB := yes + +include $(REP_DIR)/lib/mk/generate_ada_main_pkg.inc diff --git a/repos/gems/lib/mk/spec/x86_64/cbe_init.mk b/repos/gems/lib/mk/spec/x86_64/cbe_init.mk new file mode 100644 index 0000000000..be772abde3 --- /dev/null +++ b/repos/gems/lib/mk/spec/x86_64/cbe_init.mk @@ -0,0 +1,15 @@ +CBE_DIR = $(call select_from_ports,cbe) + +LIBS += spark sha256_4k cbe_common cbe_cxx_common + +INC_DIR += $(CBE_DIR)/src/lib/cbe_init + +SRC_ADB += cbe-init_library.adb +SRC_ADB += cbe-block_allocator.adb +SRC_ADB += cbe-superblock_initializer.adb +SRC_ADB += cbe-vbd_initializer.adb +SRC_ADB += cbe-free_tree_initializer.adb + +vpath % $(CBE_DIR)/src/lib/cbe_init + +CC_ADA_OPT += -gnatec=$(CBE_DIR)/src/lib/cbe_init/pragmas.adc diff --git a/repos/gems/lib/mk/spec/x86_64/cbe_init_cxx.mk b/repos/gems/lib/mk/spec/x86_64/cbe_init_cxx.mk new file mode 100644 index 0000000000..c433466473 --- /dev/null +++ b/repos/gems/lib/mk/spec/x86_64/cbe_init_cxx.mk @@ -0,0 +1,17 @@ +CBE_DIR = $(call select_from_ports,cbe) + +LIBS += spark libsparkcrypto sha256_4k cbe_common cbe_cxx_common +LIBS += cbe_init + +INC_DIR += $(CBE_DIR)/src/lib/cbe_init +INC_DIR += $(CBE_DIR)/src/lib/cbe_common +INC_DIR += $(CBE_DIR)/src/lib/cbe_init_cxx +INC_DIR += $(CBE_DIR)/src/lib/cbe_cxx_common + +SRC_ADB += cbe-cxx-cxx_init_library.adb + +vpath % $(CBE_DIR)/src/lib/cbe_init_cxx + +SHARED_LIB := yes + +include $(REP_DIR)/lib/mk/generate_ada_main_pkg.inc diff --git a/repos/gems/lib/mk/spec/x86_64/vfs_cbe.mk b/repos/gems/lib/mk/spec/x86_64/vfs_cbe.mk new file mode 100644 index 0000000000..24c12acf27 --- /dev/null +++ b/repos/gems/lib/mk/spec/x86_64/vfs_cbe.mk @@ -0,0 +1,11 @@ +SRC_CC = vfs.cc + +INC_DIR += $(REP_DIR)/src/lib/vfs/cbe + +LIBS += cbe_cxx + +vpath % $(REP_DIR)/src/lib/vfs/cbe + +SHARED_LIB := yes + +CC_CXX_WARN_STRICT := diff --git a/repos/gems/lib/mk/spec/x86_64/vfs_cbe_crypto_aes_cbc.mk b/repos/gems/lib/mk/spec/x86_64/vfs_cbe_crypto_aes_cbc.mk new file mode 100644 index 0000000000..ee780287e2 --- /dev/null +++ b/repos/gems/lib/mk/spec/x86_64/vfs_cbe_crypto_aes_cbc.mk @@ -0,0 +1,9 @@ +SRC_CC := vfs.cc +SRC_CC += aes_cbc.cc + +LIBS += aes_cbc_4k + +vpath vfs.cc $(REP_DIR)/src/lib/vfs/cbe_crypto/ +vpath % $(REP_DIR)/src/lib/vfs/cbe_crypto/aes_cbc + +SHARED_LIB = yes diff --git a/repos/gems/lib/mk/spec/x86_64/vfs_cbe_crypto_memcopy.mk b/repos/gems/lib/mk/spec/x86_64/vfs_cbe_crypto_memcopy.mk new file mode 100644 index 0000000000..7532b0449f --- /dev/null +++ b/repos/gems/lib/mk/spec/x86_64/vfs_cbe_crypto_memcopy.mk @@ -0,0 +1,7 @@ +SRC_CC := vfs.cc +SRC_CC += memcopy.cc + +vpath vfs.cc $(REP_DIR)/src/lib/vfs/cbe_crypto/ +vpath %.cc $(REP_DIR)/src/lib/vfs/cbe_crypto/memcopy + +SHARED_LIB = yes diff --git a/repos/gems/lib/mk/spec/x86_64/vfs_cbe_trust_anchor.mk b/repos/gems/lib/mk/spec/x86_64/vfs_cbe_trust_anchor.mk new file mode 100644 index 0000000000..7a7c332696 --- /dev/null +++ b/repos/gems/lib/mk/spec/x86_64/vfs_cbe_trust_anchor.mk @@ -0,0 +1,5 @@ +SRC_CC = vfs.cc + +vpath % $(REP_DIR)/src/lib/vfs/cbe_trust_anchor + +SHARED_LIB := yes diff --git a/repos/gems/lib/symbols/cbe_check_cxx b/repos/gems/lib/symbols/cbe_check_cxx new file mode 100644 index 0000000000..7b86a6742d --- /dev/null +++ b/repos/gems/lib/symbols/cbe_check_cxx @@ -0,0 +1,14 @@ +_ZN9Cbe_check11object_sizeERKNS_7LibraryE T +_ZN9Cbe_check7Library20io_request_completedERKN3Cbe9Io_buffer5IndexEb T +_ZN9Cbe_check7Library21submit_client_requestERKN3Cbe7RequestE T +_ZN9Cbe_check7Library22io_request_in_progressERKN3Cbe9Io_buffer5IndexE T +_ZN9Cbe_check7Library29drop_completed_client_requestERKN3Cbe7RequestE T +_ZN9Cbe_check7Library7executeERKN3Cbe9Io_bufferE T +_ZN9Cbe_check7LibraryC1Ev T +_ZN9Sha256_4k4hashERKNS_4DataERNS_4HashE T +_ZNK9Cbe_check7Library14has_io_requestERN3Cbe7RequestERNS1_9Io_buffer5IndexE T +_ZNK9Cbe_check7Library16execute_progressEv T +_ZNK9Cbe_check7Library25client_request_acceptableEv T +_ZNK9Cbe_check7Library29peek_completed_client_requestEv T +cbe_check_cxx_final T +cbe_check_cxx_init T diff --git a/repos/gems/lib/symbols/cbe_cxx b/repos/gems/lib/symbols/cbe_cxx new file mode 100644 index 0000000000..e91982deba --- /dev/null +++ b/repos/gems/lib/symbols/cbe_cxx @@ -0,0 +1,44 @@ +_ZN3Cbe11object_sizeERKNS_7LibraryE T +_ZN3Cbe7Library20io_request_completedERKNS_9Io_buffer5IndexEb T +_ZN3Cbe7Library21submit_client_requestERKNS_7RequestEj T +_ZN3Cbe7Library22io_request_in_progressERKNS_9Io_buffer5IndexE T +_ZN3Cbe7Library24crypto_add_key_completedERKNS_7RequestE T +_ZN3Cbe7Library24crypto_add_key_requestedERKNS_7RequestE T +_ZN3Cbe7Library24supply_crypto_plain_dataERKNS_19Crypto_plain_buffer5IndexEb T +_ZN3Cbe7Library25drop_generated_ta_requestERKNS_20Trust_anchor_requestE T +_ZN3Cbe7Library25supply_crypto_cipher_dataERKNS_20Crypto_cipher_buffer5IndexEb T +_ZN3Cbe7Library27crypto_plain_data_requestedERKNS_20Crypto_cipher_buffer5IndexE T +_ZN3Cbe7Library27crypto_remove_key_completedERKNS_7RequestE T +_ZN3Cbe7Library27crypto_remove_key_requestedERKNS_7RequestE T +_ZN3Cbe7Library28crypto_cipher_data_requestedERKNS_19Crypto_plain_buffer5IndexE T +_ZN3Cbe7Library29drop_completed_client_requestERKNS_7RequestE T +_ZN3Cbe7Library35client_transfer_read_data_completedERKNS_19Crypto_plain_buffer5IndexEb T +_ZN3Cbe7Library36client_transfer_write_data_completedERKNS_19Crypto_plain_buffer5IndexEb T +_ZN3Cbe7Library37client_transfer_read_data_in_progressERKNS_19Crypto_plain_buffer5IndexE T +_ZN3Cbe7Library38client_transfer_write_data_in_progressERKNS_19Crypto_plain_buffer5IndexE T +_ZN3Cbe7Library44mark_generated_ta_secure_sb_request_completeERKNS_20Trust_anchor_requestE T +_ZN3Cbe7Library45mark_generated_ta_create_key_request_completeERKNS_20Trust_anchor_requestERKNS_19Key_plaintext_valueE T +_ZN3Cbe7Library46mark_generated_ta_decrypt_key_request_completeERKNS_20Trust_anchor_requestERKNS_19Key_plaintext_valueE T +_ZN3Cbe7Library46mark_generated_ta_encrypt_key_request_completeERKNS_20Trust_anchor_requestERKNS_20Key_ciphertext_valueE T +_ZN3Cbe7Library47mark_generated_ta_last_sb_hash_request_completeERKNS_20Trust_anchor_requestERKNS_4HashE T +_ZN3Cbe7Library7executeERNS_9Io_bufferERNS_19Crypto_plain_bufferERNS_20Crypto_cipher_bufferE T +_ZN3Cbe7LibraryC2Ev T +_ZNK3Cbe7Library15_has_io_requestERNS_7RequestERNS_9Io_buffer5IndexE T +_ZNK3Cbe7Library16execute_progressEv T +_ZNK3Cbe7Library19active_snapshot_idsERNS_19Active_snapshot_idsE T +_ZNK3Cbe7Library24_crypto_add_key_requiredERNS_7RequestERNS_3KeyE T +_ZNK3Cbe7Library25client_request_acceptableEv T +_ZNK3Cbe7Library26_peek_generated_ta_requestERNS_20Trust_anchor_requestE T +_ZNK3Cbe7Library26_peek_generated_ta_sb_hashERKNS_20Trust_anchor_requestERNS_4HashE T +_ZNK3Cbe7Library27_crypto_plain_data_requiredERNS_7RequestERNS_20Crypto_cipher_buffer5IndexE T +_ZNK3Cbe7Library27_crypto_remove_key_requiredERNS_7RequestERNS_3Key2IdE T +_ZNK3Cbe7Library28_crypto_cipher_data_requiredERNS_7RequestERNS_19Crypto_plain_buffer5IndexE T +_ZNK3Cbe7Library29peek_completed_client_requestEv T +_ZNK3Cbe7Library34client_transfer_read_data_requiredERNS_7RequestERyRNS_19Crypto_plain_buffer5IndexE T +_ZNK3Cbe7Library35client_transfer_write_data_requiredERNS_7RequestERyRNS_19Crypto_plain_buffer5IndexE T +_ZNK3Cbe7Library38_peek_generated_ta_key_value_plaintextERKNS_20Trust_anchor_requestERNS_19Key_plaintext_valueE T +_ZNK3Cbe7Library39_peek_generated_ta_key_value_ciphertextERKNS_20Trust_anchor_requestERNS_20Key_ciphertext_valueE T +_ZNK3Cbe7Library5_infoERNS_4InfoE T +_ZNK3Cbe7Library7max_vbaEv T +cbe_cxx_final T +cbe_cxx_init T diff --git a/repos/gems/lib/symbols/cbe_dump_cxx b/repos/gems/lib/symbols/cbe_dump_cxx new file mode 100644 index 0000000000..218a8d7184 --- /dev/null +++ b/repos/gems/lib/symbols/cbe_dump_cxx @@ -0,0 +1,14 @@ +_ZN8Cbe_dump11object_sizeERKNS_7LibraryE T +_ZN8Cbe_dump7Library20io_request_completedERKN3Cbe9Io_buffer5IndexEb T +_ZN8Cbe_dump7Library21submit_client_requestERKN3Cbe7RequestERKNS_13ConfigurationE T +_ZN8Cbe_dump7Library22io_request_in_progressERKN3Cbe9Io_buffer5IndexE T +_ZN8Cbe_dump7Library29drop_completed_client_requestERKN3Cbe7RequestE T +_ZN8Cbe_dump7Library7executeERKN3Cbe9Io_bufferE T +_ZN8Cbe_dump7LibraryC1Ev T +_ZN9Sha256_4k4hashERKNS_4DataERNS_4HashE T +_ZNK8Cbe_dump7Library14has_io_requestERN3Cbe7RequestERNS1_9Io_buffer5IndexE T +_ZNK8Cbe_dump7Library16execute_progressEv T +_ZNK8Cbe_dump7Library25client_request_acceptableEv T +_ZNK8Cbe_dump7Library29peek_completed_client_requestEv T +cbe_dump_cxx_final T +cbe_dump_cxx_init T diff --git a/repos/gems/lib/symbols/cbe_init_cxx b/repos/gems/lib/symbols/cbe_init_cxx new file mode 100644 index 0000000000..bd19268849 --- /dev/null +++ b/repos/gems/lib/symbols/cbe_init_cxx @@ -0,0 +1,25 @@ +_ZN10Aes_cbc_4k7decryptERKNS_3KeyENS_12Block_numberERKNS_10CiphertextERNS_9PlaintextE T +_ZN10Aes_cbc_4k7encryptERKNS_3KeyENS_12Block_numberERKNS_9PlaintextERNS_10CiphertextE T +_ZN8Cbe_init11object_sizeERKNS_7LibraryE T +_ZN8Cbe_init7Library20io_request_completedERKN3Cbe9Io_buffer5IndexEb T +_ZN8Cbe_init7Library21submit_client_requestERKN3Cbe7RequestEyyyyyy T +_ZN8Cbe_init7Library22io_request_in_progressERKN3Cbe9Io_buffer5IndexE T +_ZN8Cbe_init7Library25drop_generated_ta_requestERKN3Cbe20Trust_anchor_requestE T +_ZN8Cbe_init7Library29drop_completed_client_requestERKN3Cbe7RequestE T +_ZN8Cbe_init7Library44mark_generated_ta_secure_sb_request_completeERKN3Cbe20Trust_anchor_requestE T +_ZN8Cbe_init7Library45mark_generated_ta_create_key_request_completeERKN3Cbe20Trust_anchor_requestERKNS1_19Key_plaintext_valueE T +_ZN8Cbe_init7Library46mark_generated_ta_decrypt_key_request_completeERKN3Cbe20Trust_anchor_requestERKNS1_19Key_plaintext_valueE T +_ZN8Cbe_init7Library46mark_generated_ta_encrypt_key_request_completeERKN3Cbe20Trust_anchor_requestERKNS1_20Key_ciphertext_valueE T +_ZN8Cbe_init7Library7executeERN3Cbe9Io_bufferE T +_ZN8Cbe_init7LibraryC1Ev T +_ZN9Sha256_4k4hashERKNS_4DataERNS_4HashE T +_ZNK8Cbe_init7Library14has_io_requestERN3Cbe7RequestERNS1_9Io_buffer5IndexE T +_ZNK8Cbe_init7Library16execute_progressEv T +_ZNK8Cbe_init7Library25client_request_acceptableEv T +_ZNK8Cbe_init7Library26_peek_generated_ta_requestERN3Cbe20Trust_anchor_requestE T +_ZNK8Cbe_init7Library26_peek_generated_ta_sb_hashERKN3Cbe20Trust_anchor_requestERNS1_4HashE T +_ZNK8Cbe_init7Library29peek_completed_client_requestEv T +_ZNK8Cbe_init7Library38_peek_generated_ta_key_value_plaintextERKN3Cbe20Trust_anchor_requestERNS1_19Key_plaintext_valueE T +_ZNK8Cbe_init7Library39_peek_generated_ta_key_value_ciphertextERKN3Cbe20Trust_anchor_requestERNS1_20Key_ciphertext_valueE T +cbe_init_cxx_final T +cbe_init_cxx_init T diff --git a/repos/gems/ports/cbe.hash b/repos/gems/ports/cbe.hash new file mode 100644 index 0000000000..c70503a85c --- /dev/null +++ b/repos/gems/ports/cbe.hash @@ -0,0 +1 @@ +7bef1d81eef5ca1e8b99305c7b32fe8302d4eead diff --git a/repos/gems/ports/cbe.port b/repos/gems/ports/cbe.port new file mode 100644 index 0000000000..ff2f62e225 --- /dev/null +++ b/repos/gems/ports/cbe.port @@ -0,0 +1,12 @@ +LICENSE := AGPLv3 +VERSION := git +DOWNLOADS := cbe.git + +URL(cbe) := https://github.com/m-stein/cbe.git +DIR(cbe) := cbe +REV(cbe) := 317ffc7c4c8779a4d3c59140b48ae461ec72df7a + +default: symlinks + +symlinks: $(DOWNLOADS) + ln -s cbe/src src diff --git a/repos/gems/recipes/pkg/cbe_check/README b/repos/gems/recipes/pkg/cbe_check/README new file mode 100644 index 0000000000..67a67f5a3b --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_check/README @@ -0,0 +1 @@ +Runtime for deploying cbe_check component from the depot. diff --git a/repos/gems/recipes/pkg/cbe_check/archives b/repos/gems/recipes/pkg/cbe_check/archives new file mode 100644 index 0000000000..9427399ae6 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_check/archives @@ -0,0 +1,6 @@ +_/src/cbe +_/src/libsparkcrypto +_/src/spark +_/src/init +_/src/vfs +_/src/vfs_block diff --git a/repos/gems/recipes/pkg/cbe_check/hash b/repos/gems/recipes/pkg/cbe_check/hash new file mode 100644 index 0000000000..5920ea2d8e --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_check/hash @@ -0,0 +1 @@ +2020-11-03 495df004fdd7a4433a783e224cc85bbcfba67593 diff --git a/repos/gems/recipes/pkg/cbe_check/runtime b/repos/gems/recipes/pkg/cbe_check/runtime new file mode 100644 index 0000000000..324ffda1c2 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_check/runtime @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repos/gems/recipes/pkg/cbe_demo/README b/repos/gems/recipes/pkg/cbe_demo/README new file mode 100644 index 0000000000..63d57a8be9 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_demo/README @@ -0,0 +1 @@ +CBE meta package diff --git a/repos/gems/recipes/pkg/cbe_demo/archives b/repos/gems/recipes/pkg/cbe_demo/archives new file mode 100644 index 0000000000..11d3a277c5 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_demo/archives @@ -0,0 +1,10 @@ +_/pkg/cbe_check +_/pkg/cbe_fs +_/pkg/cbe_ta_fs +_/pkg/cbe_init +_/pkg/cbe_shell +_/pkg/cbe_vbox5-nova +_/pkg/cbe_vfs +_/pkg/cbe_ta_vfs +_/pkg/cbe_vm_fs +_/pkg/download_coreplus diff --git a/repos/gems/recipes/pkg/cbe_demo/hash b/repos/gems/recipes/pkg/cbe_demo/hash new file mode 100644 index 0000000000..eba998a9a4 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_demo/hash @@ -0,0 +1 @@ +2020-11-03 ed2257ee606d05e5632527b4614c7c9e7e0205c9 diff --git a/repos/gems/recipes/pkg/cbe_fs/README b/repos/gems/recipes/pkg/cbe_fs/README new file mode 100644 index 0000000000..468fa0442d --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_fs/README @@ -0,0 +1,2 @@ +Runtime for deploying the chroot component configured for the CBE from +the depot. diff --git a/repos/gems/recipes/pkg/cbe_fs/archives b/repos/gems/recipes/pkg/cbe_fs/archives new file mode 100644 index 0000000000..380ffb610d --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_fs/archives @@ -0,0 +1 @@ +_/src/chroot diff --git a/repos/gems/recipes/pkg/cbe_fs/hash b/repos/gems/recipes/pkg/cbe_fs/hash new file mode 100644 index 0000000000..ccb0fb74d9 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_fs/hash @@ -0,0 +1 @@ +2020-11-03 d13cb17d3fb6d2b939403c60b59748a83bdb68fb diff --git a/repos/gems/recipes/pkg/cbe_fs/runtime b/repos/gems/recipes/pkg/cbe_fs/runtime new file mode 100644 index 0000000000..f85e905eae --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_fs/runtime @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/repos/gems/recipes/pkg/cbe_init/README b/repos/gems/recipes/pkg/cbe_init/README new file mode 100644 index 0000000000..9420631387 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_init/README @@ -0,0 +1 @@ +Runtime for deploying cbe_init component from the depot. diff --git a/repos/gems/recipes/pkg/cbe_init/archives b/repos/gems/recipes/pkg/cbe_init/archives new file mode 100644 index 0000000000..9427399ae6 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_init/archives @@ -0,0 +1,6 @@ +_/src/cbe +_/src/libsparkcrypto +_/src/spark +_/src/init +_/src/vfs +_/src/vfs_block diff --git a/repos/gems/recipes/pkg/cbe_init/hash b/repos/gems/recipes/pkg/cbe_init/hash new file mode 100644 index 0000000000..2aa1280668 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_init/hash @@ -0,0 +1 @@ +2020-11-03 49f70906f96793c36b0363f14d78792c0abe1667 diff --git a/repos/gems/recipes/pkg/cbe_init/runtime b/repos/gems/recipes/pkg/cbe_init/runtime new file mode 100644 index 0000000000..f6ee91ac50 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_init/runtime @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repos/gems/recipes/pkg/cbe_shell/README b/repos/gems/recipes/pkg/cbe_shell/README new file mode 100644 index 0000000000..959626248e --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_shell/README @@ -0,0 +1 @@ +Unix runtime that allows the user to interact with the CBE. diff --git a/repos/gems/recipes/pkg/cbe_shell/archives b/repos/gems/recipes/pkg/cbe_shell/archives new file mode 100644 index 0000000000..1b8e26c22e --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_shell/archives @@ -0,0 +1,11 @@ +_/src/bash-minimal +_/src/coreutils +_/src/libc +_/src/posix +_/src/ncurses +_/src/gui_fb +_/src/terminal +_/src/init +_/src/vfs +_/src/vfs_pipe +_/src/cached_fs_rom diff --git a/repos/gems/recipes/pkg/cbe_shell/hash b/repos/gems/recipes/pkg/cbe_shell/hash new file mode 100644 index 0000000000..55d95358e3 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_shell/hash @@ -0,0 +1 @@ +2020-11-03 5b89c2d378281c2fb2cfb74edfb2cd439e13b90b diff --git a/repos/gems/recipes/pkg/cbe_shell/runtime b/repos/gems/recipes/pkg/cbe_shell/runtime new file mode 100644 index 0000000000..1d64c20ade --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_shell/runtime @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2018-01-01 00:01 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repos/gems/recipes/pkg/cbe_ta_fs/README b/repos/gems/recipes/pkg/cbe_ta_fs/README new file mode 100644 index 0000000000..e68c3b7efb --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_ta_fs/README @@ -0,0 +1,2 @@ +Runtime for deploying the chroot component configured for the CBE +trust-anchor from the depot. diff --git a/repos/gems/recipes/pkg/cbe_ta_fs/archives b/repos/gems/recipes/pkg/cbe_ta_fs/archives new file mode 100644 index 0000000000..380ffb610d --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_ta_fs/archives @@ -0,0 +1 @@ +_/src/chroot diff --git a/repos/gems/recipes/pkg/cbe_ta_fs/hash b/repos/gems/recipes/pkg/cbe_ta_fs/hash new file mode 100644 index 0000000000..dda3570e0c --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_ta_fs/hash @@ -0,0 +1 @@ +2020-11-03 8e4e4c9286335360b754ec50ae12acaf43385e99 diff --git a/repos/gems/recipes/pkg/cbe_ta_fs/runtime b/repos/gems/recipes/pkg/cbe_ta_fs/runtime new file mode 100644 index 0000000000..484fbb8a07 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_ta_fs/runtime @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/repos/gems/recipes/pkg/cbe_ta_vfs/README b/repos/gems/recipes/pkg/cbe_ta_vfs/README new file mode 100644 index 0000000000..8a166ccc73 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_ta_vfs/README @@ -0,0 +1 @@ +Runtime for deploying the CBE trust-anchor vfs component from the depot. diff --git a/repos/gems/recipes/pkg/cbe_ta_vfs/archives b/repos/gems/recipes/pkg/cbe_ta_vfs/archives new file mode 100644 index 0000000000..d9dc16e34b --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_ta_vfs/archives @@ -0,0 +1,2 @@ +_/src/vfs +_/src/cbe diff --git a/repos/gems/recipes/pkg/cbe_ta_vfs/hash b/repos/gems/recipes/pkg/cbe_ta_vfs/hash new file mode 100644 index 0000000000..4e53ddcdeb --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_ta_vfs/hash @@ -0,0 +1 @@ +2020-11-03 b8e50b12ac45cae558da999afc6beac8dec19893 diff --git a/repos/gems/recipes/pkg/cbe_ta_vfs/runtime b/repos/gems/recipes/pkg/cbe_ta_vfs/runtime new file mode 100644 index 0000000000..ac277ba040 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_ta_vfs/runtime @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repos/gems/recipes/pkg/cbe_vbox5-nova/README b/repos/gems/recipes/pkg/cbe_vbox5-nova/README new file mode 100644 index 0000000000..a8f0d16a46 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_vbox5-nova/README @@ -0,0 +1,2 @@ + + VirtualBox runtime for using a VM with Block session disk access diff --git a/repos/gems/recipes/pkg/cbe_vbox5-nova/archives b/repos/gems/recipes/pkg/cbe_vbox5-nova/archives new file mode 100644 index 0000000000..9e565f4c16 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_vbox5-nova/archives @@ -0,0 +1,9 @@ +_/src/vbox5-nova +_/src/base-nova +_/src/libc +_/src/init +_/src/posix +_/src/zlib +_/src/libiconv +_/src/stdcxx +_/src/vfs diff --git a/repos/gems/recipes/pkg/cbe_vbox5-nova/hash b/repos/gems/recipes/pkg/cbe_vbox5-nova/hash new file mode 100644 index 0000000000..02e3517782 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_vbox5-nova/hash @@ -0,0 +1 @@ +2020-11-03 59f5c91d5f4ceb803ba54a5fa476e70957522d4f diff --git a/repos/gems/recipes/pkg/cbe_vbox5-nova/runtime b/repos/gems/recipes/pkg/cbe_vbox5-nova/runtime new file mode 100644 index 0000000000..1f2c893130 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_vbox5-nova/runtime @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repos/gems/recipes/pkg/cbe_vfs/README b/repos/gems/recipes/pkg/cbe_vfs/README new file mode 100644 index 0000000000..46f2c42b33 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_vfs/README @@ -0,0 +1,2 @@ +Runtime for deploying the vfs component configured for the CBE from +the depot. diff --git a/repos/gems/recipes/pkg/cbe_vfs/archives b/repos/gems/recipes/pkg/cbe_vfs/archives new file mode 100644 index 0000000000..ea1d63c737 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_vfs/archives @@ -0,0 +1,4 @@ +_/src/vfs +_/src/cbe +_/src/spark +_/src/libcrypto diff --git a/repos/gems/recipes/pkg/cbe_vfs/hash b/repos/gems/recipes/pkg/cbe_vfs/hash new file mode 100644 index 0000000000..9536bf5368 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_vfs/hash @@ -0,0 +1 @@ +2020-11-03 fac7defdc3e6e631cb2e24386c42d29fa2447151 diff --git a/repos/gems/recipes/pkg/cbe_vfs/runtime b/repos/gems/recipes/pkg/cbe_vfs/runtime new file mode 100644 index 0000000000..ae8c8a4683 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_vfs/runtime @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repos/gems/recipes/pkg/cbe_vm_fs/README b/repos/gems/recipes/pkg/cbe_vm_fs/README new file mode 100644 index 0000000000..e7ef7938d8 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_vm_fs/README @@ -0,0 +1,2 @@ +Runtime for deploying the chroot component configured for the VM +running on top the CBE from the depot. diff --git a/repos/gems/recipes/pkg/cbe_vm_fs/archives b/repos/gems/recipes/pkg/cbe_vm_fs/archives new file mode 100644 index 0000000000..380ffb610d --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_vm_fs/archives @@ -0,0 +1 @@ +_/src/chroot diff --git a/repos/gems/recipes/pkg/cbe_vm_fs/hash b/repos/gems/recipes/pkg/cbe_vm_fs/hash new file mode 100644 index 0000000000..366b76e780 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_vm_fs/hash @@ -0,0 +1 @@ +2020-11-03 203f5c9953bf13e046452ae9e3cd6694a35c4bfa diff --git a/repos/gems/recipes/pkg/cbe_vm_fs/runtime b/repos/gems/recipes/pkg/cbe_vm_fs/runtime new file mode 100644 index 0000000000..71b6f40a51 --- /dev/null +++ b/repos/gems/recipes/pkg/cbe_vm_fs/runtime @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/repos/gems/recipes/pkg/download_coreplus/README b/repos/gems/recipes/pkg/download_coreplus/README new file mode 100644 index 0000000000..26aefef574 --- /dev/null +++ b/repos/gems/recipes/pkg/download_coreplus/README @@ -0,0 +1 @@ +This package is used to prepare the assets for the TinyCore+ VM. diff --git a/repos/gems/recipes/pkg/download_coreplus/archives b/repos/gems/recipes/pkg/download_coreplus/archives new file mode 100644 index 0000000000..23b85407a8 --- /dev/null +++ b/repos/gems/recipes/pkg/download_coreplus/archives @@ -0,0 +1,20 @@ +_/raw/download_coreplus +_/src/libc +_/src/vfs_lxip +_/src/bash-minimal +_/src/coreutils-minimal +_/src/curl +_/src/fetchurl +_/src/init +_/src/libcrypto +_/src/libssh +_/src/libssl +_/src/ncurses +_/src/gui_fb +_/src/posix +_/src/report_rom +_/src/terminal +_/src/terminal_log +_/src/vfs +_/src/zlib +_/src/cached_fs_rom diff --git a/repos/gems/recipes/pkg/download_coreplus/hash b/repos/gems/recipes/pkg/download_coreplus/hash new file mode 100644 index 0000000000..3b5abbaaef --- /dev/null +++ b/repos/gems/recipes/pkg/download_coreplus/hash @@ -0,0 +1 @@ +2020-11-03 0b391915cc6c8905cb1840f7716cf9d58742e8f1 diff --git a/repos/gems/recipes/pkg/download_coreplus/runtime b/repos/gems/recipes/pkg/download_coreplus/runtime new file mode 100644 index 0000000000..21bec87756 --- /dev/null +++ b/repos/gems/recipes/pkg/download_coreplus/runtime @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repos/gems/recipes/raw/download_coreplus/content.mk b/repos/gems/recipes/raw/download_coreplus/content.mk new file mode 100644 index 0000000000..a2c1295614 --- /dev/null +++ b/repos/gems/recipes/raw/download_coreplus/content.mk @@ -0,0 +1,8 @@ +content: init.config assets.tar + +init.config: + cp $(REP_DIR)/recipes/raw/download_coreplus/$@ $@ + +assets.tar: + tar --mtime='2018-05-29 00:00Z' -cf $@ -C $(REP_DIR)/recipes/raw/download_coreplus machine.vbox + tar --mtime='2018-05-29 00:00Z' -rf $@ -C $(REP_DIR)/recipes/raw/download_coreplus disk0.vmdk diff --git a/repos/gems/recipes/raw/download_coreplus/disk0.vmdk b/repos/gems/recipes/raw/download_coreplus/disk0.vmdk new file mode 100644 index 0000000000..a01b0f261b --- /dev/null +++ b/repos/gems/recipes/raw/download_coreplus/disk0.vmdk @@ -0,0 +1,24 @@ +# Disk DescriptorFile +version=1 +CID=e2776b25 +parentCID=ffffffff +createType="fullDevice" + +# Extent description +RW 2097152 FLAT "/dev/cbe/data" 0 + +# The disk Data Base +#DDB + +ddb.virtualHWVersion = "4" +ddb.adapterType="ide" +ddb.geometry.cylinders="16383" +ddb.geometry.heads="16" +ddb.geometry.sectors="63" +ddb.uuid.image="05efe683-df7a-43e0-b8f8-0ee9f689a785" +ddb.uuid.parent="00000000-0000-0000-0000-000000000000" +ddb.uuid.modification="254ef0be-7de8-419f-961b-03ae474d08ce" +ddb.uuid.parentmodification="00000000-0000-0000-0000-000000000000" +ddb.geometry.biosCylinders="1024" +ddb.geometry.biosHeads="255" +ddb.geometry.biosSectors="63" diff --git a/repos/gems/recipes/raw/download_coreplus/hash b/repos/gems/recipes/raw/download_coreplus/hash new file mode 100644 index 0000000000..ee3109de2e --- /dev/null +++ b/repos/gems/recipes/raw/download_coreplus/hash @@ -0,0 +1 @@ +2020-08-20-m d6fe379ab9410910da99798bb9b8264fc34be587 diff --git a/repos/gems/recipes/raw/download_coreplus/init.config b/repos/gems/recipes/raw/download_coreplus/init.config new file mode 100644 index 0000000000..8f1275035e --- /dev/null +++ b/repos/gems/recipes/raw/download_coreplus/init.config @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +cp /machine.vbox /disk0.vmdk /vm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2019-07-04 12:00 + 01234567890123456789 + + + + + + + + + + + + + + + + + diff --git a/repos/gems/recipes/raw/download_coreplus/machine.vbox b/repos/gems/recipes/raw/download_coreplus/machine.vbox new file mode 100644 index 0000000000..1c0a4a74e4 --- /dev/null +++ b/repos/gems/recipes/raw/download_coreplus/machine.vbox @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repos/gems/recipes/src/cbe/content.mk b/repos/gems/recipes/src/cbe/content.mk new file mode 100644 index 0000000000..b447f03590 --- /dev/null +++ b/repos/gems/recipes/src/cbe/content.mk @@ -0,0 +1,74 @@ +BUILD_LIBS := \ + cbe_cxx \ + cbe_init_cxx \ + cbe_dump_cxx \ + cbe_check_cxx + +MIRROR_FROM_CBE_DIR := \ + src/lib/cbe \ + src/lib/cbe_common \ + src/lib/cbe_cxx \ + src/lib/cbe_cxx_common \ + src/lib/cbe_init \ + src/lib/cbe_init_cxx \ + src/lib/cbe_dump \ + src/lib/cbe_dump_cxx \ + src/lib/cbe_check \ + src/lib/cbe_check_cxx \ + src/lib/sha256_4k + +MIRROR_FROM_REP_DIR := \ + lib/import/import-cbe.mk \ + lib/import/import-cbe_common.mk \ + lib/import/import-cbe_init.mk \ + lib/import/import-cbe_dump.mk \ + lib/import/import-cbe_check.mk \ + lib/import/import-sha256_4k.mk \ + lib/mk/spec/x86_64/cbe.mk \ + lib/mk/spec/x86_64/cbe_common.mk \ + lib/mk/spec/x86_64/cbe_cxx.mk \ + lib/mk/spec/x86_64/cbe_cxx_common.mk \ + lib/mk/spec/x86_64/cbe_init.mk \ + lib/mk/spec/x86_64/cbe_init_cxx.mk \ + lib/mk/spec/x86_64/cbe_dump.mk \ + lib/mk/spec/x86_64/cbe_dump_cxx.mk \ + lib/mk/spec/x86_64/cbe_check.mk \ + lib/mk/spec/x86_64/cbe_check_cxx.mk \ + lib/mk/spec/x86_64/vfs_cbe.mk \ + lib/mk/spec/x86_64/vfs_cbe_crypto_aes_cbc.mk \ + lib/mk/spec/x86_64/vfs_cbe_crypto_memcopy.mk \ + lib/mk/spec/x86_64/vfs_cbe_trust_anchor.mk \ + lib/mk/generate_ada_main_pkg.inc \ + lib/mk/sha256_4k.mk \ + lib/symbols/cbe_check_cxx \ + lib/symbols/cbe_dump_cxx \ + lib/symbols/cbe_init_cxx \ + src/lib/vfs/cbe \ + src/lib/vfs/cbe_crypto/vfs.cc \ + src/lib/vfs/cbe_crypto/aes_cbc \ + src/lib/vfs/cbe_crypto/memcopy \ + src/lib/vfs/cbe_trust_anchor \ + src/app/cbe_check \ + src/app/cbe_dump \ + src/app/cbe_init \ + src/app/cbe_tester \ + include/cbe + +CBE_DIR := $(call port_dir,$(REP_DIR)/ports/cbe) + +content: $(MIRROR_FROM_REP_DIR) $(MIRROR_FROM_CBE_DIR) LICENSE src/lib/cbe/target.mk + +$(MIRROR_FROM_REP_DIR): + $(mirror_from_rep_dir) + +$(MIRROR_FROM_CBE_DIR): + mkdir -p $(dir $@) + cp -r $(CBE_DIR)/$@ $(dir $@) + +src/lib/cbe/target.mk: + mkdir -p $(dir $@) + echo "REQUIRES += x86_64" > $@ + echo "LIBS += $(BUILD_LIBS)" >> $@ + +LICENSE: + cp $(GENODE_DIR)/LICENSE $@ diff --git a/repos/gems/recipes/src/cbe/hash b/repos/gems/recipes/src/cbe/hash new file mode 100644 index 0000000000..f807df81aa --- /dev/null +++ b/repos/gems/recipes/src/cbe/hash @@ -0,0 +1 @@ +2020-11-23-v b0420c400f8d8f766d55f96837f867600f8e22b9 diff --git a/repos/gems/recipes/src/cbe/used_apis b/repos/gems/recipes/src/cbe/used_apis new file mode 100644 index 0000000000..756710d24c --- /dev/null +++ b/repos/gems/recipes/src/cbe/used_apis @@ -0,0 +1,9 @@ +base +block_session +os +vfs +aes_cbc_4k +libcrypto +so +spark +libsparkcrypto diff --git a/repos/gems/run/cbe_tester.run b/repos/gems/run/cbe_tester.run new file mode 100644 index 0000000000..86674e0bf2 --- /dev/null +++ b/repos/gems/run/cbe_tester.run @@ -0,0 +1,1042 @@ + +assert_spec linux + +exec rm -rf var/libcache/cbe +exec rm -rf var/libcache/cbe_cxx +exec rm -rf var/libcache/cbe_common +exec rm -rf var/libcache/cbe_common_cxx +exec rm -rf var/libcache/cbe_init +exec rm -rf var/libcache/cbe_init_cxx +exec rm -rf var/libcache/cbe_check +exec rm -rf var/libcache/cbe_check_cxx +exec rm -rf var/libcache/cbe_dump +exec rm -rf var/libcache/cbe_dump_cxx + +set dd [installed_command dd] + +proc cbe_image_file { } { + return "cbe_block.img" +} + +set image_size 1024 +if {[info exists ::env(CBE_IMAGE_SIZE)]} { + set image_size $::env(CBE_IMAGE_SIZE) +} + +proc cbe_image_size_mb { } { + global image_size + return $image_size +} + +proc cbe_vbd_size_mb { } { + return [expr [cbe_image_size_mb] / 2] +} + +proc cbe_ft_size_mb { } { + return [expr [cbe_image_size_mb] / 8] +} + +append build_components { + core + init + timer + server/vfs + app/cbe_tester + server/log_terminal + server/report_rom + server/lx_fs + server/lx_block + lib/vfs/cbe_crypto/aes_cbc + lib/vfs/cbe_trust_anchor + lib/vfs/import +} + +build $build_components + +create_boot_directory + +append config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +install_config $config + +exec rm -rf bin/[cbe_image_file] + +catch { exec $dd if=/dev/urandom of=bin/[cbe_image_file] bs=1M count=[cbe_image_size_mb] } + +append boot_modules { + core + init + timer + cbe_tester + ld.lib.so spark.lib.so + libsparkcrypto.lib.so + cbe_cxx.lib.so + cbe_check_cxx.lib.so + cbe_dump_cxx.lib.so + cbe_init_cxx.lib.so + log_terminal + report_rom + lx_fs + vfs + lx_block + libc.lib.so + libcrypto.lib.so + vfs.lib.so + vfs_cbe_trust_anchor.lib.so + vfs_cbe_crypto_aes_cbc.lib.so + vfs_import.lib.so +} + +append boot_modules [cbe_image_file] + +build_boot_image $boot_modules + +append qemu_args " -nographic -m 1280 " + +run_genode_until "child \"test\" exited with exit value.*\n" 300 +grep_output {\[init\] child "test" exited with exit value} +compare_output_to {[init] child "test" exited with exit value 0} diff --git a/repos/gems/run/vfs_cbe.run b/repos/gems/run/vfs_cbe.run new file mode 100644 index 0000000000..b72ed8ed3f --- /dev/null +++ b/repos/gems/run/vfs_cbe.run @@ -0,0 +1,361 @@ +assert_spec linux + +if {[expr ![info exists ::env(CBE_KEEP)]]} { +exec rm -rf var/libcache/cbe +exec rm -rf var/libcache/cbe_cxx +exec rm -rf var/libcache/cbe_common +exec rm -rf var/libcache/cbe_common_cxx +exec rm -rf var/libcache/cbe_init +exec rm -rf var/libcache/cbe_init_cxx +exec rm -rf var/libcache/cbe_check +exec rm -rf var/libcache/cbe_check_cxx +exec rm -rf var/libcache/cbe_dump +exec rm -rf var/libcache/cbe_dump_cxx +} + +create_boot_directory + +proc cbe_image_file { } { + return "vfs_cbe_block.img" +} + +set use_interactively [expr ![get_cmd_switch --autopilot]] + +import_from_depot [depot_user]/pkg/[drivers_interactive_pkg] \ + [depot_user]/pkg/terminal \ + [depot_user]/src/ncurses \ + [depot_user]/src/bash \ + [depot_user]/src/coreutils \ + [depot_user]/src/nitpicker \ + [depot_user]/src/gui_fb + + +build { + core + timer + init + server/lx_fs + lib/vfs/cbe + lib/vfs/cbe_crypto/aes_cbc + lib/vfs/cbe_crypto/memcopy + lib/vfs/cbe_trust_anchor + lib/vfs/import + lib/vfs/pipe + test/vfs_stress + test/libc + server/log_terminal + server/report_rom + server/fs_rom +} + +set config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } + +append_if $use_interactively config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } + +append_if [expr !$use_interactively] config { + + + + + + + + } + +append config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2018-01-01 00:01 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +install_config $config + +set shell_script "run/vfs_cbe.sh" +set repo "[repository_contains $shell_script]" +exec cp $repo/$shell_script bin/ + +append boot_modules { + lx_fs + cbe_init_trust_anchor + spark.lib.so + libsparkcrypto.lib.so + cbe_cxx.lib.so + vfs_cbe.lib.so + vfs_cbe_crypto_aes_cbc.lib.so + vfs_cbe_crypto_memcopy.lib.so + vfs_cbe_trust_anchor.lib.so + vfs_cbe.sh + vfs.lib.so + vfs_import.lib.so + vfs_pipe.lib.so + posix.lib.so + libc.lib.so + libcrypto.lib.so + log_terminal + fs_rom + report_rom + sequence + init + core + timer + ld.lib.so +} + +append boot_modules { + keyfile secured_superblock +} +append boot_modules [cbe_image_file] + +set fd [open [run_dir]/genode/focus w] +puts $fd " \" domain=\"default\"/>" +close $fd + +build_boot_image $boot_modules + +if {$use_interactively} { + run_genode_until forever +} else { + run_genode_until {.*--- Automated CBE testing finished.*} 1800 +} diff --git a/repos/gems/run/vfs_cbe.sh b/repos/gems/run/vfs_cbe.sh new file mode 100644 index 0000000000..e333fcb4d3 --- /dev/null +++ b/repos/gems/run/vfs_cbe.sh @@ -0,0 +1,252 @@ +#!/bin/bash + +echo "--- Automated CBE testing ---" + +produce_pattern() { + local pattern="$1" + local size="$2" + [ "$pattern" = "" ] && exit 1 + + local tmp_file="/tmp/pattern.tmp" + local N=1041 + # prints numbers until N and uses pattern as delimiter and + # generates about 4 KiB of data with a 1 byte pattern + seq -s "$pattern" $N > $tmp_file + dd if=$tmp_file count=1 bs=$size 2>/dev/null +} + +test_write_1() { + local data_file="$1" + local offset=$2 + + local pattern_file="/tmp/pattern" + dd bs=4096 count=1 if=$pattern_file of=$data_file seek=$offset 2>/dev/null || exit 1 +} + +test_read_compare_1() { + local data_file="$1" + local offset=$2 + + local pattern_file="/tmp/pattern" + rm $pattern_file.out 2>/dev/null + + dd bs=4096 count=1 if=$data_file of=$pattern_file.out skip=$offset 2>/dev/null || exit 1 + local sha1=$(sha1sum $pattern_file) + local sha1_sum=${sha1:0:40} + + local sha1out=$(sha1sum $pattern_file.out) + local sha1out_sum=${sha1out:0:40} + + if [ "$sha1_sum" != "$sha1out_sum" ]; then + echo "mismatch for block $offset:" + echo " expected: $sha1_sum" + echo -n " " + dd if=$pattern_file bs=32 count=1 2>/dev/null; echo + echo " got: $sha1out_sum" + echo -n " " + dd if=$pattern_file.out bs=32 count=1 2>/dev/null; echo + return 1 + fi +} + +test_create_snapshot() { + local cbe_dir="$1" + + echo "Create snapshot" + echo true > $cbe_dir/control/create_snapshot +} + +test_list_snapshots() { + local cbe_dir="$1" + + echo "List content of '$cbe_dir'" + ls -l $cbe_dir/snapshots +} + +test_discard_snapshot() { + local cbe_dir="$1" + local snap_id=$2 + + echo "Discard snapshot with id: $snap_id" + echo $snap_id > $cbe_dir/control/discard_snapshot +} + +test_rekey() { + local cbe_dir="$1" + + echo "Start rekeying" + echo on > $cbe_dir/control/rekey + echo "Reykeying started" +} + +test_vbd_extension() { + local cbe_dir="$1" + local nr_of_phys_blocks="$2" + + echo "Start extending VBD" + echo tree=vbd, blocks=$nr_of_phys_blocks > $cbe_dir/control/extend + echo "VBD extension started" +} + +test_ft_extension() { + local cbe_dir="$1" + local nr_of_phys_blocks="$2" + + echo "Start extending FT" + echo tree=ft, blocks=$nr_of_phys_blocks > $cbe_dir/control/extend + echo "FT extension started" +} + +test_rekey_state() { + local cbe_dir="$1" + local state="$(< $cbe_dir/control/rekey)" + + echo "Rekeying state: $state" +} + +test_rekey_state() { + local cbe_dir="$1" + local state="$(< $cbe_dir/control/rekey)" + + echo "Rekeying state: $state" +} + +wait_for_rekeying() { + local cbe_dir="$1" + + echo "Wait for rekeying to finish..." + while : ; do + local file_content="$(< $cbe_dir/control/rekey)" + local state="${file_content:0:4}" + if [ "$state" = "idle" ]; then + local result="${file_content:5}" + echo "Reykeying done: $result" + break; + fi + sleep 2 + done +} + +wait_for_vbd_extension() { + local cbe_dir="$1" + + echo "Wait for VBD extension to finish..." + while : ; do + local file_content="$(< $cbe_dir/control/extend)" + local state="${file_content:0:4}" + if [ "$state" = "idle" ]; then + local result="${file_content:5}" + echo "VBD extension done: $result" + break; + fi + sleep 2 + done +} + +wait_for_ft_extension() { + local cbe_dir="$1" + + echo "Wait for FT extension to finish..." + while : ; do + local file_content="$(< $cbe_dir/control/extend)" + local state="${file_content:0:4}" + if [ "$state" = "idle" ]; then + local result="${file_content:5}" + echo "VBD extension done: $result" + break; + fi + sleep 2 + done +} + +main() { + local cbe_dir="/dev/cbe" + local data_file="$cbe_dir/current/data" + + ls -l $cbe_dir + + for i in $(seq 3); do + + echo "--> Run $i:" + + local pattern_file="/tmp/pattern" + produce_pattern "$i" "4096" > $pattern_file + + test_write_1 "$data_file" "419" + test_write_1 "$data_file" "63" + test_write_1 "$data_file" "333" + + test_vbd_extension "$cbe_dir" "1000" + test_read_compare_1 "$data_file" "63" + test_write_1 "$data_file" "175" + test_read_compare_1 "$data_file" "419" + test_write_1 "$data_file" "91" + test_read_compare_1 "$data_file" "175" + test_read_compare_1 "$data_file" "91" + test_read_compare_1 "$data_file" "333" + wait_for_vbd_extension "$cbe_dir" + + test_write_1 "$data_file" "32" + test_write_1 "$data_file" "77" + test_write_1 "$data_file" "199" + + test_ft_extension "$cbe_dir" "1000" + test_read_compare_1 "$data_file" "32" + test_write_1 "$data_file" "211" + test_read_compare_1 "$data_file" "77" + test_write_1 "$data_file" "278" + test_read_compare_1 "$data_file" "199" + test_read_compare_1 "$data_file" "278" + test_read_compare_1 "$data_file" "211" + wait_for_ft_extension "$cbe_dir" + + test_write_1 "$data_file" "0" + test_write_1 "$data_file" "8" + test_write_1 "$data_file" "16" + test_write_1 "$data_file" "490" + test_write_1 "$data_file" "468" + + test_read_compare_1 "$data_file" "0" + test_read_compare_1 "$data_file" "8" + test_read_compare_1 "$data_file" "16" + test_read_compare_1 "$data_file" "490" + + test_rekey "$cbe_dir" + test_write_1 "$data_file" "0" + test_rekey_state "$cbe_dir" + test_read_compare_1 "$data_file" "490" + test_rekey_state "$cbe_dir" + test_write_1 "$data_file" "16" + test_rekey_state "$cbe_dir" + test_read_compare_1 "$data_file" "468" + test_rekey_state "$cbe_dir" + test_read_compare_1 "$data_file" "8" + test_rekey_state "$cbe_dir" + test_read_compare_1 "$data_file" "16" + test_rekey_state "$cbe_dir" + test_read_compare_1 "$data_file" "0" + test_write_1 "$data_file" "300" + test_write_1 "$data_file" "240" + test_write_1 "$data_file" "201" + test_write_1 "$data_file" "328" + wait_for_rekeying "$cbe_dir" + + echo "--> Run $i done" + + done + + echo "--> Read/Compare test" + test_read_compare_1 "$data_file" "0" + test_read_compare_1 "$data_file" "490" + test_read_compare_1 "$data_file" "468" + test_read_compare_1 "$data_file" "8" + test_read_compare_1 "$data_file" "16" + echo "--> Read/Compare test done" + + echo "--- Automated CBE testing finished, shell is yours ---" +} + +main "$@" + +# just drop into shell +# exit 0 diff --git a/repos/gems/run/vfs_cbe_init.run b/repos/gems/run/vfs_cbe_init.run new file mode 100644 index 0000000000..bb9070c5bf --- /dev/null +++ b/repos/gems/run/vfs_cbe_init.run @@ -0,0 +1,176 @@ +assert_spec linux + +if {[expr ![info exists ::env(CBE_KEEP)]]} { +exec rm -rf var/libcache/cbe +exec rm -rf var/libcache/cbe_cxx +exec rm -rf var/libcache/cbe_common +exec rm -rf var/libcache/cbe_common_cxx +exec rm -rf var/libcache/cbe_init +exec rm -rf var/libcache/cbe_init_cxx +exec rm -rf var/libcache/cbe_check +exec rm -rf var/libcache/cbe_check_cxx +exec rm -rf var/libcache/cbe_dump +exec rm -rf var/libcache/cbe_dump_cxx +} + +proc cbe_image_file { } { + return "vfs_cbe_block.img" +} + +set image_size 32 + +proc cbe_image_size_mb { } { + global image_size + return $image_size +} + +append build_components { + core init timer + server/lx_block + server/lx_fs + server/vfs + app/sequence + + app/cbe_init_trust_anchor + app/cbe_init + lib/vfs/cbe_trust_anchor +} + +build $build_components + +create_boot_directory + +append config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +install_config $config + +exec rm -rf bin/[cbe_image_file] +exec truncate -s [cbe_image_size_mb]M bin/[cbe_image_file] + +append boot_modules { + core init timer lx_block lx_fs sequence + vfs vfs.lib.so + ld.lib.so spark.lib.so libsparkcrypto.lib.so + + cbe_init_trust_anchor + cbe_init cbe_init_cxx.lib.so + vfs_cbe_trust_anchor.lib.so +} + +append boot_modules [cbe_image_file] + +build_boot_image $boot_modules + +run_genode_until {.*child "initialize_cbe" exited with exit value 0.*\n} 240 + +exec cp [run_dir]/genode/keyfile bin +exec cp [run_dir]/genode/secured_superblock bin diff --git a/repos/gems/src/app/cbe_check/README b/repos/gems/src/app/cbe_check/README new file mode 100644 index 0000000000..c51da4f9ea --- /dev/null +++ b/repos/gems/src/app/cbe_check/README @@ -0,0 +1,19 @@ +The cbe_check component check the meta data of a CBE device. This includes +the tree for the virtual block device, free tree and meta tree. On success, +the component exits with exit value 0, otherwise with exit value -1. + + +Sessions +~~~~~~~~ + +This is an overview of the sessions required and provided by the +component apart from the environment sessions: + +* Requires one Block session. + + +Examples +~~~~~~~~ + +An example of how to use the cbe_init component can be found in the test script +'cbe/run/cbe_check.run'. diff --git a/repos/gems/src/app/cbe_check/config.xsd b/repos/gems/src/app/cbe_check/config.xsd new file mode 100644 index 0000000000..4a70c93716 --- /dev/null +++ b/repos/gems/src/app/cbe_check/config.xsd @@ -0,0 +1,7 @@ + + + + + + + diff --git a/repos/gems/src/app/cbe_check/main.cc b/repos/gems/src/app/cbe_check/main.cc new file mode 100644 index 0000000000..36d0675db4 --- /dev/null +++ b/repos/gems/src/app/cbe_check/main.cc @@ -0,0 +1,215 @@ +/* + * \brief Integration of the Consistent Block Encrypter (CBE) + * \author Martin Stein + * \author Josef Soentgen + * \date 2020-11-10 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* Genode includes */ +#include +#include +#include +#include + +#include + +/* CBE-init includes */ +#include + +using namespace Genode; + +class Main +{ + private: + + enum { TX_BUF_SIZE = Block::Session::TX_QUEUE_SIZE * Cbe::BLOCK_SIZE }; + + Env &_env; + Heap _heap { _env.ram(), _env.rm() }; + Allocator_avl _blk_alloc { &_heap }; + Block::Connection<> _blk { _env, &_blk_alloc, TX_BUF_SIZE }; + Signal_handler
_blk_handler { _env.ep(), *this, &Main::_execute }; + Cbe::Request _blk_req { }; + Cbe::Io_buffer _blk_buf { }; + Cbe_check::Library _cbe_check { }; + + Genode::size_t _blk_ratio { + Cbe::BLOCK_SIZE / _blk.info().block_size }; + + void _execute() + { + for (bool progress { true }; progress; ) { + + progress = false; + + _cbe_check.execute(_blk_buf); + if (_cbe_check.execute_progress()) { + progress = true; + } + + Cbe::Request const req { + _cbe_check.peek_completed_client_request() }; + + if (req.valid()) { + _cbe_check.drop_completed_client_request(req); + if (req.success()) { + _env.parent().exit(0); + } else { + error("request was not successful");; + _env.parent().exit(-1); + } + } + + struct Invalid_io_request : Exception { }; + + while (_blk.tx()->ready_to_submit()) { + + Cbe::Io_buffer::Index data_index { 0 }; + Cbe::Request request { }; + _cbe_check.has_io_request(request, data_index); + + if (!request.valid()) { + break; + } + if (_blk_req.valid()) { + break; + } + try { + request.tag(data_index.value); + Block::Packet_descriptor::Opcode op; + switch (request.operation()) { + case Cbe::Request::Operation::READ: + op = Block::Packet_descriptor::READ; + break; + case Cbe::Request::Operation::WRITE: + op = Block::Packet_descriptor::WRITE; + break; + default: + throw Invalid_io_request(); + } + Block::Packet_descriptor packet { + _blk.alloc_packet(Cbe::BLOCK_SIZE), op, + request.block_number() * _blk_ratio, + request.count() * _blk_ratio }; + + if (request.operation() == Cbe::Request::Operation::WRITE) { + *reinterpret_cast( + _blk.tx()->packet_content(packet)) = + _blk_buf.item(data_index); + } + _blk.tx()->try_submit_packet(packet); + _blk_req = request; + _cbe_check.io_request_in_progress(data_index); + progress = true; + } + catch (Block::Session::Tx::Source::Packet_alloc_failed) { + break; + } + } + + while (_blk.tx()->ack_avail()) { + + Block::Packet_descriptor packet = + _blk.tx()->try_get_acked_packet(); + + if (!_blk_req.valid()) { + break; + } + + bool const read = + packet.operation() == Block::Packet_descriptor::READ; + + bool const write = + packet.operation() == Block::Packet_descriptor::WRITE; + + bool const op_match = + (read && _blk_req.read()) || + (write && _blk_req.write()); + + bool const bn_match = + packet.block_number() / _blk_ratio == _blk_req.block_number(); + + if (!bn_match || !op_match) { + break; + } + + _blk_req.success(packet.succeeded()); + + Cbe::Io_buffer::Index const data_index { _blk_req.tag() }; + bool const success { _blk_req.success() }; + + if (read && success) { + _blk_buf.item(data_index) = + *reinterpret_cast( + _blk.tx()->packet_content(packet)); + } + _cbe_check.io_request_completed(data_index, success); + _blk.tx()->release_packet(packet); + _blk_req = Cbe::Request(); + progress = true; + } + } + _blk.tx()->wakeup(); + } + + public: + + Main(Env &env) + : + _env { env } + { + if (_blk_ratio == 0) { + error("backend block size not supported"); + _env.parent().exit(-1); + return; + } + + if (!_cbe_check.client_request_acceptable()) { + error("failed to submit request"); + _env.parent().exit(-1); + } + _cbe_check.submit_client_request( + Cbe::Request( + Cbe::Request::Operation::READ, + false, 0, 0, 0, 0, 0)); + + _blk.tx_channel()->sigh_ack_avail(_blk_handler); + _blk.tx_channel()->sigh_ready_to_submit(_blk_handler); + + _execute(); + } + + ~Main() + { + _blk.tx_channel()->sigh_ack_avail(Signal_context_capability()); + _blk.tx_channel()->sigh_ready_to_submit(Signal_context_capability()); + } +}; + +extern "C" int memcmp(const void *p0, const void *p1, Genode::size_t size) +{ + return Genode::memcmp(p0, p1, size); +} + +extern "C" void adainit(); + +void Component::construct(Genode::Env &env) +{ + env.exec_static_constructors(); + Timer::Connection timer { env }; + timer.msleep(3000); + Genode::log("start checking"); + + Cbe::assert_valid_object_size(); + + cbe_check_cxx_init(); + + static Main main(env); +} diff --git a/repos/gems/src/app/cbe_check/target.mk b/repos/gems/src/app/cbe_check/target.mk new file mode 100644 index 0000000000..5cff2a6914 --- /dev/null +++ b/repos/gems/src/app/cbe_check/target.mk @@ -0,0 +1,9 @@ +REQUIRES += x86_64 + +TARGET := cbe_check + +SRC_CC += main.cc +INC_DIR += $(PRG_DIR) +LIBS += base cbe_check_cxx + +CONFIG_XSD = config.xsd diff --git a/repos/gems/src/app/cbe_dump/README b/repos/gems/src/app/cbe_dump/README new file mode 100644 index 0000000000..9abf54e2b1 --- /dev/null +++ b/repos/gems/src/app/cbe_dump/README @@ -0,0 +1,72 @@ +The cbe_dump component dumps the meta-data of a CBE device (virtual block +device, free tree and meta tree) according to the configured parameters. +On success, the component exits with exit value 0, otherwise with exit +value -1. + + +Configuration +~~~~~~~~~~~~~ + +This is an example configuration of the component that will dump the +last valid superblock and the its last valid snapshot: + +! +! +! + +This is a short description of the tags and attributes: + +:config.free_tree: + Dump free tree nodes. + +:config.meta_tree: + Dump meta tree nodes. + +:config.hashes: + Show hashes in all tree dumps. + +:config.max_superblocks: + Max number of superblocks to dump. + +:config.max_snapshots: + Max number of snapshot to dump per superblock. + +:config.unused_nodes: + Show unused nodes in all tree dumps. + +:config.vbd: + Dump virtual block device tree nodes. + +:config.vbd_pba_filter: + Only show sub-tree where the PBA matches. + +:config.vbd_pba_filter_enabled: + Enable PBA filter. + +:config.vbd_vba_filter: + Only show sub-tree where the VBA matches. + +:config.vbd_vba_filter_enabled: + Enable VBA filter. + + +Sessions +~~~~~~~~ + +This is an overview of the sessions required and provided by the +component apart from the environment sessions: + +* Requires one Block session. + + +Examples +~~~~~~~~ + +An example of how to use the cbe_dump component can be found in the test script +'cbe/run/cbe_dump.run'. diff --git a/repos/gems/src/app/cbe_dump/config.xsd b/repos/gems/src/app/cbe_dump/config.xsd new file mode 100644 index 0000000000..5740405e35 --- /dev/null +++ b/repos/gems/src/app/cbe_dump/config.xsd @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repos/gems/src/app/cbe_dump/main.cc b/repos/gems/src/app/cbe_dump/main.cc new file mode 100644 index 0000000000..7ce1722ebf --- /dev/null +++ b/repos/gems/src/app/cbe_dump/main.cc @@ -0,0 +1,231 @@ +/* + * \brief Integration of the Consistent Block Encrypter (CBE) + * \author Martin Stein + * \author Josef Soentgen + * \date 2020-11-10 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* CBE-dump includes */ +#include + +using namespace Genode; + + +class Main +{ + private: + + enum { TX_BUF_SIZE = Block::Session::TX_QUEUE_SIZE * Cbe::BLOCK_SIZE }; + + Env &_env; + Heap _heap { _env.ram(), _env.rm() }; + Allocator_avl _blk_alloc { &_heap }; + Block::Connection<> _blk { _env, &_blk_alloc, TX_BUF_SIZE }; + Signal_handler
_blk_handler { _env.ep(), *this, &Main::_execute }; + Cbe::Request _blk_req { }; + Cbe::Io_buffer _blk_buf { }; + Cbe_dump::Library _cbe_dump { }; + + Genode::size_t _blk_ratio { + Cbe::BLOCK_SIZE / _blk.info().block_size }; + + void _execute() + { + for (bool progress { true }; progress; ) { + + progress = false; + + _cbe_dump.execute(_blk_buf); + if (_cbe_dump.execute_progress()) { + progress = true; + } + + Cbe::Request const req { + _cbe_dump.peek_completed_client_request() }; + + if (req.valid()) { + _cbe_dump.drop_completed_client_request(req); + if (req.success()) { + _env.parent().exit(0); + } else { + error("request was not successful");; + _env.parent().exit(-1); + } + } + + struct Invalid_io_request : Exception { }; + + while (_blk.tx()->ready_to_submit()) { + + Cbe::Io_buffer::Index data_index { 0 }; + Cbe::Request request { }; + _cbe_dump.has_io_request(request, data_index); + + if (!request.valid()) { + break; + } + if (_blk_req.valid()) { + break; + } + try { + request.tag(data_index.value); + Block::Packet_descriptor::Opcode op; + switch (request.operation()) { + case Cbe::Request::Operation::READ: + op = Block::Packet_descriptor::READ; + break; + case Cbe::Request::Operation::WRITE: + op = Block::Packet_descriptor::WRITE; + break; + default: + throw Invalid_io_request(); + } + Block::Packet_descriptor packet { + _blk.alloc_packet(Cbe::BLOCK_SIZE), op, + request.block_number() * _blk_ratio, + request.count() * _blk_ratio }; + + if (request.operation() == Cbe::Request::Operation::WRITE) { + *reinterpret_cast( + _blk.tx()->packet_content(packet)) = + _blk_buf.item(data_index); + } + _blk.tx()->try_submit_packet(packet); + _blk_req = request; + _cbe_dump.io_request_in_progress(data_index); + progress = true; + } + catch (Block::Session::Tx::Source::Packet_alloc_failed) { + break; + } + } + + while (_blk.tx()->ack_avail()) { + + Block::Packet_descriptor packet = + _blk.tx()->try_get_acked_packet(); + + if (!_blk_req.valid()) { + break; + } + + bool const read = + packet.operation() == Block::Packet_descriptor::READ; + + bool const write = + packet.operation() == Block::Packet_descriptor::WRITE; + + bool const op_match = + (read && _blk_req.read()) || + (write && _blk_req.write()); + + bool const bn_match = + packet.block_number() / _blk_ratio == _blk_req.block_number(); + + if (!bn_match || !op_match) { + break; + } + + _blk_req.success(packet.succeeded()); + + Cbe::Io_buffer::Index const data_index { _blk_req.tag() }; + bool const success { _blk_req.success() }; + + if (read && success) { + _blk_buf.item(data_index) = + *reinterpret_cast( + _blk.tx()->packet_content(packet)); + } + _cbe_dump.io_request_completed(data_index, success); + _blk.tx()->release_packet(packet); + _blk_req = Cbe::Request { }; + progress = true; + } + } + _blk.tx()->wakeup(); + } + + public: + + Main(Env &env) + : + _env { env } + { + if (_blk_ratio == 0) { + error("backend block size not supported"); + _env.parent().exit(-1); + return; + } + + if (!_cbe_dump.client_request_acceptable()) { + error("failed to submit request"); + _env.parent().exit(-1); + } + + Attached_rom_dataspace config_rom { _env, "config" }; + + try { + Xml_node const &config { + config_rom.xml().sub_node("dump") }; + + Cbe_dump::Configuration const cfg { config }; + + _cbe_dump.submit_client_request( + Cbe::Request { Cbe::Request::Operation::READ, + false, 0, 0, 0, 0, 0 }, + cfg); + } + catch (Xml_node::Nonexistent_sub_node) { + error("missing 'dump' config node"); + _env.parent().exit(1); + return; + } + + _blk.tx_channel()->sigh_ack_avail(_blk_handler); + _blk.tx_channel()->sigh_ready_to_submit(_blk_handler); + + _execute(); + } + + ~Main() + { + _blk.tx_channel()->sigh_ack_avail(Signal_context_capability()); + _blk.tx_channel()->sigh_ready_to_submit(Signal_context_capability()); + } +}; + + +extern "C" int memcmp(const void *p0, const void *p1, Genode::size_t size) +{ + return Genode::memcmp(p0, p1, size); +} + + +extern "C" void adainit(); + + +void Component::construct(Genode::Env &env) +{ + env.exec_static_constructors(); + Timer::Connection timer { env }; + + Cbe::assert_valid_object_size(); + + cbe_dump_cxx_init(); + + static Main main(env); +} diff --git a/repos/gems/src/app/cbe_dump/target.mk b/repos/gems/src/app/cbe_dump/target.mk new file mode 100644 index 0000000000..3b1870c2cb --- /dev/null +++ b/repos/gems/src/app/cbe_dump/target.mk @@ -0,0 +1,9 @@ +REQUIRES += x86_64 + +TARGET := cbe_dump + +SRC_CC += main.cc +INC_DIR += $(PRG_DIR) +LIBS += base cbe_dump_cxx + +CONFIG_XSD = config.xsd diff --git a/repos/gems/src/app/cbe_init/README b/repos/gems/src/app/cbe_init/README new file mode 100644 index 0000000000..7fb8c27615 --- /dev/null +++ b/repos/gems/src/app/cbe_init/README @@ -0,0 +1,75 @@ +The cbe_init component creates a fresh CBE device (virtual block device, +free tree and superblocks) on a back-end block-device according to the +configured parameters. On success, the component exits with exit value 0, +otherwise with exit value -1. + + +Configuration +~~~~~~~~~~~~~ + +This is an example configuration of the component which shows the default +value for each attribute except 'config.dst_ip' and 'config.interface': + +! +! +! +! +! + +This is a short description of the tags and attributes: + +:config.virtual-block-device: + Mandatory. Contains parameters for the tree that represents the virtual + block device. + +:config.virtual-block-device.nr_of_levels: + Mandatory. Number of tree levels including the leafs and the root. + +:config.virtual-block-device.nr_of_children: + Mandatory. The tree degree or number of children for each inner node of the + tree. + +:config.virtual-block-device.nr_of_leafs: + Mandatory. The number of leafs of the tree that are used. This defines the + number of virtual blocks available to the CBE device. This number must be + less or equal the number of leafs of the tree, which results from the number + of tree levels and the tree degree. + +:config.free-tree: + Mandatory. Contains parameters for the tree that is used for managing the + spare blocks of the CBE device. + +:config.free-tree.nr_of_levels: + Mandatory. Number of tree levels including the leafs and the root. + +:config.free-tree.nr_of_children: + Mandatory. The tree degree or number of children for each inner node of the + tree. + +:config.free-tree.nr_of_leafs: + Mandatory. The number of leafs of the tree that are used. This defines the + number of spare blocks available to the CBE device. This number must be less + or equal the number of leafs of the tree, which results from the number of + tree levels and the tree degree. + + +Sessions +~~~~~~~~ + +This is an overview of the sessions required and provided by the +component apart from the environment sessions: + +* Requires one Block session. + + +Examples +~~~~~~~~ + +An example of how to use the cbe_init component can be found in the test script +'cbe/run/cbe_init_dev.run'. diff --git a/repos/gems/src/app/cbe_init/config.xsd b/repos/gems/src/app/cbe_init/config.xsd new file mode 100644 index 0000000000..fe328b95eb --- /dev/null +++ b/repos/gems/src/app/cbe_init/config.xsd @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repos/gems/src/app/cbe_init/main.cc b/repos/gems/src/app/cbe_init/main.cc new file mode 100644 index 0000000000..c5b414511c --- /dev/null +++ b/repos/gems/src/app/cbe_init/main.cc @@ -0,0 +1,372 @@ +/* + * \brief Integration of the Consistent Block Encrypter (CBE) + * \author Martin Stein + * \author Josef Soentgen + * \date 2020-11-10 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* CBE includes */ +#include +#include +#include + + +using namespace Genode; + +class Main +{ + private: + + enum { TX_BUF_SIZE = Block::Session::TX_QUEUE_SIZE * Cbe::BLOCK_SIZE }; + + Env &_env; + Heap _heap { _env.ram(), _env.rm() }; + + Attached_rom_dataspace _config_rom { _env, "config" }; + + Allocator_avl _blk_alloc { &_heap }; + Block::Connection<> _blk { _env, &_blk_alloc, TX_BUF_SIZE }; + Signal_handler
_blk_handler { _env.ep(), *this, &Main::_execute }; + Cbe::Request _blk_req { }; + Cbe::Io_buffer _blk_buf { }; + Cbe_init::Library _cbe_init { }; + + Genode::size_t _blk_ratio { + Cbe::BLOCK_SIZE / _blk.info().block_size }; + + Vfs::Simple_env _vfs_env { _env, _heap, _config_rom.xml().sub_node("vfs") }; + Vfs::File_system &_vfs { _vfs_env.root_dir() }; + + static Util::Trust_anchor_vfs::Path _config_ta_dir(Xml_node const &node) + { + using String_path = Genode::String<1024>; + String_path const path = + node.attribute_value("trust_anchor_dir", String_path()); + + if (!path.valid()) { + error("missing mandatory 'trust_anchor_dir' config attribute"); + struct Missing_config_attribute { }; + throw Missing_config_attribute(); + } + + return Util::Trust_anchor_vfs::Path { path.string() }; + } + + Util::Trust_anchor_vfs _trust_anchor { + _vfs, _vfs_env.alloc(), _config_ta_dir(_config_rom.xml()), + _blk_handler }; + + bool _execute_trust_anchor() + { + bool progress = _trust_anchor.execute(); + + using Op = Cbe::Trust_anchor_request::Operation; + + while (true) { + + Cbe::Trust_anchor_request const request = + _cbe_init.peek_generated_ta_request(); + + if (!request.valid()) { break; } + if (!_trust_anchor.request_acceptable()) { break; } + + switch (request.operation()) { + case Op::CREATE_KEY: + _trust_anchor.submit_create_key_request(request); + break; + case Op::SECURE_SUPERBLOCK: + { + Cbe::Hash const sb_hash = _cbe_init.peek_generated_ta_sb_hash(request); + _trust_anchor.submit_secure_superblock_request(request, sb_hash); + break; + } + case Op::ENCRYPT_KEY: + { + Cbe::Key_plaintext_value const pk = + _cbe_init.peek_generated_ta_key_value_plaintext(request); + + _trust_anchor.submit_encrypt_key_request(request, pk); + break; + } + case Op::DECRYPT_KEY: + { + Cbe::Key_ciphertext_value const ck = + _cbe_init.peek_generated_ta_key_value_ciphertext(request); + + _trust_anchor.submit_decrypt_key_request(request, ck); + break; + } + case Op::LAST_SB_HASH: + break; + case Op::INITIALIZE: + class Bad_operation { }; + throw Bad_operation { }; + case Op::INVALID: + /* never reached */ + break; + } + _cbe_init.drop_generated_ta_request(request); + progress |= true; + } + + while (true) { + + Cbe::Trust_anchor_request const request = + _trust_anchor.peek_completed_request(); + + if (!request.valid()) { break; } + + switch (request.operation()) { + case Op::CREATE_KEY: + { + Cbe::Key_plaintext_value const pk = + _trust_anchor.peek_completed_key_value_plaintext(request); + + _cbe_init.mark_generated_ta_create_key_request_complete(request, pk); + break; + } + case Op::SECURE_SUPERBLOCK: + { + _cbe_init.mark_generated_ta_secure_sb_request_complete(request); + break; + } + case Op::ENCRYPT_KEY: + { + Cbe::Key_ciphertext_value const ck = + _trust_anchor.peek_completed_key_value_ciphertext(request); + + _cbe_init.mark_generated_ta_encrypt_key_request_complete(request, ck); + break; + } + case Op::DECRYPT_KEY: + { + Cbe::Key_plaintext_value const pk = + _trust_anchor.peek_completed_key_value_plaintext(request); + + _cbe_init.mark_generated_ta_decrypt_key_request_complete(request, pk); + break; + } + case Op::LAST_SB_HASH: + break; + case Op::INITIALIZE: + class Bad_operation { }; + throw Bad_operation { }; + case Op::INVALID: + /* never reached */ + break; + } + _trust_anchor.drop_completed_request(request); + progress |= true; + } + + return progress; + } + + void _execute() + { + for (bool progress { true }; progress; ) { + + progress = false; + + _cbe_init.execute(_blk_buf); + if (_cbe_init.execute_progress()) { + progress = true; + } + + Cbe::Request const req { + _cbe_init.peek_completed_client_request() }; + + if (req.valid()) { + _cbe_init.drop_completed_client_request(req); + if (req.success()) { + log("CBE initialization finished"); + _env.parent().exit(0); + } else { + error("request was not successful");; + _env.parent().exit(-1); + } + } + + progress |= _execute_trust_anchor(); + + struct Invalid_io_request : Exception { }; + + while (_blk.tx()->ready_to_submit()) { + + Cbe::Io_buffer::Index data_index { 0 }; + Cbe::Request request { }; + _cbe_init.has_io_request(request, data_index); + + if (!request.valid()) { + break; + } + if (_blk_req.valid()) { + break; + } + try { + request.tag(data_index.value); + Block::Packet_descriptor::Opcode op; + switch (request.operation()) { + case Cbe::Request::Operation::READ: + op = Block::Packet_descriptor::READ; + break; + case Cbe::Request::Operation::WRITE: + op = Block::Packet_descriptor::WRITE; + break; + case Cbe::Request::Operation::SYNC: + op = Block::Packet_descriptor::SYNC; + break; + default: + throw Invalid_io_request(); + } + Block::Packet_descriptor packet { + _blk.alloc_packet(Cbe::BLOCK_SIZE), op, + request.block_number() * _blk_ratio, + request.count() * _blk_ratio }; + + if (request.operation() == Cbe::Request::Operation::WRITE) { + *reinterpret_cast( + _blk.tx()->packet_content(packet)) = + _blk_buf.item(data_index); + } + _blk.tx()->try_submit_packet(packet); + _blk_req = request; + _cbe_init.io_request_in_progress(data_index); + progress = true; + } + catch (Block::Session::Tx::Source::Packet_alloc_failed) { + break; + } + } + + while (_blk.tx()->ack_avail()) { + + Block::Packet_descriptor packet = + _blk.tx()->try_get_acked_packet(); + + if (!_blk_req.valid()) { + break; + } + + bool const read = + packet.operation() == Block::Packet_descriptor::READ; + + bool const write = + packet.operation() == Block::Packet_descriptor::WRITE; + + bool const sync = + packet.operation() == Block::Packet_descriptor::SYNC; + + bool const op_match = + (read && _blk_req.read()) || + (write && _blk_req.write()) || + (sync && _blk_req.sync()); + + bool const bn_match = + packet.block_number() / _blk_ratio == _blk_req.block_number(); + + if (!bn_match || !op_match) { + break; + } + + _blk_req.success(packet.succeeded()); + + Cbe::Io_buffer::Index const data_index { _blk_req.tag() }; + bool const success { _blk_req.success() }; + + if (read && success) { + _blk_buf.item(data_index) = + *reinterpret_cast( + _blk.tx()->packet_content(packet)); + } + _cbe_init.io_request_completed(data_index, success); + _blk.tx()->release_packet(packet); + _blk_req = Cbe::Request(); + progress = true; + } + } + _blk.tx()->wakeup(); + } + + public: + + Main(Env &env) + : + _env { env } + { + if (_blk_ratio == 0) { + error("backend block size not supported"); + _env.parent().exit(-1); + return; + } + + Xml_node const &config { _config_rom.xml() }; + try { + Cbe_init::Configuration const cfg { config }; + if (!_cbe_init.client_request_acceptable()) { + error("failed to submit request"); + _env.parent().exit(-1); + } + _cbe_init.submit_client_request( + Cbe::Request( + Cbe::Request::Operation::READ, + false, 0, 0, 0, 0, 0), + cfg.vbd_nr_of_lvls() - 1, + cfg.vbd_nr_of_children(), + cfg.vbd_nr_of_leafs(), + cfg.ft_nr_of_lvls() - 1, + cfg.ft_nr_of_children(), + cfg.ft_nr_of_leafs()); + + _blk.tx_channel()->sigh_ack_avail(_blk_handler); + _blk.tx_channel()->sigh_ready_to_submit(_blk_handler); + + _execute(); + } + catch (Cbe_init::Configuration::Invalid) { + error("bad configuration"); + _env.parent().exit(-1); + } + } + + ~Main() + { + _blk.tx_channel()->sigh_ack_avail(Signal_context_capability()); + _blk.tx_channel()->sigh_ready_to_submit(Signal_context_capability()); + } +}; + +extern "C" int memcmp(const void *p0, const void *p1, Genode::size_t size) +{ + return Genode::memcmp(p0, p1, size); +} + +extern "C" void adainit(); + +void Component::construct(Genode::Env &env) +{ + env.exec_static_constructors(); + + Cbe::assert_valid_object_size(); + + cbe_init_cxx_init(); + + static Main main(env); +} diff --git a/repos/gems/src/app/cbe_init/target.mk b/repos/gems/src/app/cbe_init/target.mk new file mode 100644 index 0000000000..13efefc8e0 --- /dev/null +++ b/repos/gems/src/app/cbe_init/target.mk @@ -0,0 +1,9 @@ +REQUIRES += x86_64 + +TARGET := cbe_init + +SRC_CC += main.cc +INC_DIR += $(PRG_DIR) +LIBS += base vfs cbe_init_cxx + +CONFIG_XSD = config.xsd diff --git a/repos/gems/src/app/cbe_init_trust_anchor/component.cc b/repos/gems/src/app/cbe_init_trust_anchor/component.cc new file mode 100644 index 0000000000..9731ed5b17 --- /dev/null +++ b/repos/gems/src/app/cbe_init_trust_anchor/component.cc @@ -0,0 +1,260 @@ +/* + * \brief Integration of the Consistent Block Encrypter (CBE) + * \author Martin Stein + * \author Josef Soentgen + * \date 2020-11-10 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* CBE includes */ +#include + + +using namespace Genode; + +class Main +{ + public: + + struct Missing_config_attribute : Genode::Exception { }; + + private: + + Env &_env; + Heap _heap { _env.ram(), _env.rm() }; + + Attached_rom_dataspace _config_rom { _env, "config" }; + + Vfs::Simple_env _vfs_env { _env, _heap, _config_rom.xml().sub_node("vfs") }; + Vfs::File_system &_vfs { _vfs_env.root_dir() }; + + using Passphrase = Genode::String<32 + 1>; + + using String_path = Genode::String<256>; + + static String_path _config_ta_dir(Xml_node const &node) + { + String_path const path = + node.attribute_value("trust_anchor_dir", String_path()); + + if (!path.valid()) { + error("missing mandatory 'trust_anchor_dir' config attribute"); + throw Missing_config_attribute(); + } + return path; + } + + struct Io_response_handler : Vfs::Io_response_handler + { + Genode::Signal_context_capability _io_sigh; + + Io_response_handler(Genode::Signal_context_capability io_sigh) + : _io_sigh(io_sigh) { } + + void read_ready_response() override { } + + void io_progress_response() override + { + if (_io_sigh.valid()) { + Genode::Signal_transmitter(_io_sigh).submit(); + } + } + }; + + enum class State { WRITE, READ, }; + State _state { State::WRITE }; + + struct File + { + struct Could_not_open_file : Genode::Exception { }; + + struct Completed + { + bool complete; + bool success; + }; + + File(File const &) = delete; + File &operator=(File const&) = delete; + + Vfs::File_system &_vfs; + Vfs::Vfs_handle *_vfs_handle; + + Io_response_handler &_io_response_handler; + + Genode::Constructible _io_job { }; + Util::Io_job::Buffer _io_buffer { }; + + Passphrase _passphrase { }; + + File(char const *base_path, + char const *name, + Vfs::File_system &vfs, + Genode::Allocator &alloc, + Io_response_handler &io_response_handler) + : + _vfs { vfs }, + _vfs_handle { nullptr }, + _io_response_handler { io_response_handler } + { + using Result = Vfs::Directory_service::Open_result; + + Genode::Path<256> file_path = base_path; + file_path.append_element(name); + + Result const res = + _vfs.open(file_path.string(), + Vfs::Directory_service::OPEN_MODE_RDWR, + (Vfs::Vfs_handle **)&_vfs_handle, alloc); + if (res != Result::OPEN_OK) { + error("could not open '", file_path.string(), "'"); + throw Could_not_open_file(); + } + + _vfs_handle->handler(&io_response_handler); + } + + ~File() + { + _vfs.close(_vfs_handle); + } + + void write_passphrase(Passphrase const &passphrase) + { + /* copy */ + _passphrase = passphrase; + + _io_buffer = { + .base = const_cast(_passphrase.string()), + .size = _passphrase.length() + }; + + _io_job.construct(*_vfs_handle, Util::Io_job::Operation::WRITE, + _io_buffer, 0); + + _io_response_handler.io_progress_response(); + } + + void queue_read() + { + _io_buffer = { + .base = nullptr, + .size = 0, + }; + + _io_job.construct(*_vfs_handle, Util::Io_job::Operation::READ, + _io_buffer, 0); + + _io_response_handler.io_progress_response(); + } + + void execute() + { + if (!_io_job.constructed()) { + return; + } + + _io_job->execute(); + } + + Completed write_complete() + { + return { _io_job->completed(), _io_job->succeeded() }; + } + + Completed read_complete() + { + return { _io_job->completed(), _io_job->succeeded() }; + } + + void drop_io_job() + { + _io_job.destruct(); + } + }; + + Genode::Constructible _init_file { }; + + Genode::Signal_handler
_io_handler { + _env.ep(), *this, &Main::_handle_io }; + + void _handle_io() + { + _init_file->execute(); + + File::Completed result { false, false }; + + switch (_state) { + case State::WRITE: + result = _init_file->write_complete(); + if (result.complete) { + _init_file->drop_io_job(); + + _state = State::READ; + _init_file->queue_read(); + } + break; + case State::READ: + result = _init_file->read_complete(); + if (result.complete) { + _init_file->drop_io_job(); + _init_file.destruct(); + + Genode::log("Initialization finished successfully"); + + _env.parent().exit(result.success ? 0 : 1); + return; + } + break; + } + } + + Io_response_handler _io_response_handler { _io_handler }; + + public: + + Main(Env &env) + : + _env { env } + { + Xml_node const &config { _config_rom.xml() }; + + Passphrase const passphrase = + config.attribute_value("passphrase", Passphrase()); + if (!passphrase.valid()) { + error("mandatory 'passphrase' attribute missing"); + throw Missing_config_attribute(); + } + + String_path ta_dir = _config_ta_dir(_config_rom.xml()); + + _init_file.construct(ta_dir.string(), "initialize", + _vfs, _vfs_env.alloc(), + _io_response_handler); + + /* kick-off writing */ + _init_file->write_passphrase(passphrase.string()); + } +}; + + +void Component::construct(Genode::Env &env) +{ + static Main main(env); +} diff --git a/repos/gems/src/app/cbe_init_trust_anchor/target.mk b/repos/gems/src/app/cbe_init_trust_anchor/target.mk new file mode 100644 index 0000000000..96355216cc --- /dev/null +++ b/repos/gems/src/app/cbe_init_trust_anchor/target.mk @@ -0,0 +1,8 @@ +REQUIRES += x86_64 + +TARGET:= cbe_init_trust_anchor + +SRC_CC := component.cc +INC_DIR := $(PRG_DIR) + +LIBS := base vfs diff --git a/repos/gems/src/app/cbe_tester/README b/repos/gems/src/app/cbe_tester/README new file mode 100644 index 0000000000..f44ec33c2f --- /dev/null +++ b/repos/gems/src/app/cbe_tester/README @@ -0,0 +1,35 @@ + + ================================== + Reference manual of the CBE tester + ================================== + + Martin Stein + + + +--------------+ +----------+ + | Command Pool |<--->| Main | + +--------------+ | | + | | + +--------------+ | | + | CBE |<--->| | + +--------------+ | | + | | + +--------------+ | | + | CBE Init |<--->| | + +--------------+ | | + | | + +--------------+ | | + | CBE Dump |<--->| | + +--------------+ | | + | | + +--------------+ | | + | Block IO |<--->| | + +--------------+ | | + | | + +--------------+ | | + | Crypto |<--->| | + +--------------+ | | + | | + +--------------+ | | + | Trust Anchor |<--->| | + +--------------+ +----------+ diff --git a/repos/gems/src/app/cbe_tester/crypto.cc b/repos/gems/src/app/cbe_tester/crypto.cc new file mode 100644 index 0000000000..9968efef63 --- /dev/null +++ b/repos/gems/src/app/cbe_tester/crypto.cc @@ -0,0 +1,354 @@ +/* + * \brief Implementation of the Crypto module API using the Crypto VFS API + * \author Martin Stein + * \date 2020-10-29 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* local includes */ +#include + +using namespace Genode; +using namespace Cbe; +using namespace Vfs; + + +Crypto::Key_directory &Crypto::_get_unused_key_dir() +{ + for (Key_directory &key_dir : _key_dirs) { + if (key_dir.key_id == 0) { + return key_dir; + } + } + class Failed { }; + throw Failed { }; +} + + +Crypto::Key_directory &Crypto::_lookup_key_dir(uint32_t key_id) +{ + for (Key_directory &key_dir : _key_dirs) { + if (key_dir.key_id == key_id) { + return key_dir; + } + } + class Failed { }; + throw Failed { }; +} + + +Crypto::Crypto(Vfs::Env &env, + Xml_node const &crypto, + Signal_context_capability sigh) +: + _env { env }, + _path { crypto.attribute_value("path", String<32>()) }, + _add_key_handle { vfs_open_wo(env, { _path.string(), "/add_key" }) }, + _remove_key_handle { vfs_open_wo(env, { _path.string(), "/remove_key" }) }, + _vfs_io_response_handler { sigh } +{ } + + +bool Crypto::request_acceptable() const +{ + return _job.op == Operation::INVALID; +} + + +Crypto::Result Crypto::add_key(Key const &key) +{ + char buffer[sizeof (key.value) + sizeof (key.id.value)] { }; + memcpy(buffer, &key.id.value, sizeof (key.id.value)); + memcpy(buffer + sizeof (key.id.value), + key.value, sizeof (key.value)); + + _add_key_handle.seek(0); + file_size nr_of_written_bytes { 0 }; + try { + _add_key_handle.fs().write( + &_add_key_handle, buffer, sizeof (buffer), + nr_of_written_bytes); + + } catch (File_io_service::Insufficient_buffer) { + + return Result::RETRY_LATER; + } + Key_directory &key_dir { _get_unused_key_dir() }; + + key_dir.encrypt_handle = &vfs_open_rw( + _env, { _path.string(), "/keys/", key.id.value, "/encrypt" }); + + key_dir.decrypt_handle = &vfs_open_rw( + _env, { _path.string(), "/keys/", key.id.value, "/decrypt" }); + + key_dir.key_id = key.id.value; + key_dir.encrypt_handle->handler(&_vfs_io_response_handler); + key_dir.decrypt_handle->handler(&_vfs_io_response_handler); + return Result::SUCCEEDED; +} + + +Crypto::Result Crypto::remove_key(Cbe::Key::Id key_id) +{ + Vfs::file_size written = 0; + _remove_key_handle.seek(0); + try { + _remove_key_handle.fs().write(&_remove_key_handle, + (char const*)&key_id.value, + sizeof (key_id.value), + written); + (void)written; + + } catch (Vfs::File_io_service::Insufficient_buffer) { + + return Result::RETRY_LATER; + } + Key_directory &key_dir { _lookup_key_dir(key_id.value) }; + _env.root_dir().close(key_dir.encrypt_handle); + key_dir.encrypt_handle = nullptr; + _env.root_dir().close(key_dir.decrypt_handle); + key_dir.decrypt_handle = nullptr; + key_dir.key_id = 0; + return Result::SUCCEEDED; +} + + +void Crypto::submit_request(Cbe::Request const &request, + Operation op, + Crypto_plain_buffer::Index plain_buf_idx, + Crypto_cipher_buffer::Index cipher_buf_idx) +{ + switch (op) { + case Operation::ENCRYPT_BLOCK: + + _job.request = request; + _job.state = Job_state::SUBMITTED; + _job.op = op; + _job.cipher_buf_idx = cipher_buf_idx; + _job.plain_buf_idx = plain_buf_idx; + _job.handle = + _lookup_key_dir(request.key_id()).encrypt_handle; + + break; + + case Operation::DECRYPT_BLOCK: + + _job.request = request; + _job.state = Job_state::SUBMITTED; + _job.op = op; + _job.cipher_buf_idx = cipher_buf_idx; + _job.plain_buf_idx = plain_buf_idx; + _job.handle = + _lookup_key_dir(request.key_id()).decrypt_handle; + + break; + + case Operation::INVALID: + + class Bad_operation { }; + throw Bad_operation { }; + } +} + + +Cbe::Request Crypto::peek_completed_encryption_request() const +{ + if (_job.state != Job_state::COMPLETE || + _job.op != Operation::ENCRYPT_BLOCK) { + + return Cbe::Request { }; + } + return _job.request; +} + + +Cbe::Request Crypto::peek_completed_decryption_request() const +{ + if (_job.state != Job_state::COMPLETE || + _job.op != Operation::DECRYPT_BLOCK) { + + return Cbe::Request { }; + } + return _job.request; +} + + +void Crypto::drop_completed_request() +{ + if (_job.state != Job_state::COMPLETE) { + + class Bad_state { }; + throw Bad_state { }; + } + _job.op = Operation::INVALID; +} + + +void Crypto::_execute_decrypt_block(Job &job, + Crypto_plain_buffer &plain_buf, + Crypto_cipher_buffer &cipher_buf, + bool &progress) +{ + switch (job.state) { + case Job_state::SUBMITTED: + { + job.handle->seek(job.request.block_number() * Cbe::BLOCK_SIZE); + file_size nr_of_written_bytes { 0 }; + try { + job.handle->fs().write( + job.handle, + reinterpret_cast( + &cipher_buf.item(job.cipher_buf_idx)), + file_size(sizeof (Cbe::Block_data)), + nr_of_written_bytes); + + job.state = Job_state::OP_WRITTEN_TO_VFS_HANDLE; + progress = true; + return; + + } catch (Vfs::File_io_service::Insufficient_buffer) { + + return; + } + } + case Job_state::OP_WRITTEN_TO_VFS_HANDLE: + { + job.handle->seek(job.request.block_number() * Cbe::BLOCK_SIZE); + bool const success { + job.handle->fs().queue_read( + job.handle, sizeof (Cbe::Block_data)) }; + + if (!success) { + return; + } + job.state = Job_state::READING_VFS_HANDLE_SUCCEEDED; + progress = true; + break; + } + case Job_state::READING_VFS_HANDLE_SUCCEEDED: + { + file_size nr_of_read_bytes { 0 }; + Read_result const result = + job.handle->fs().complete_read( + job.handle, + reinterpret_cast( + &plain_buf.item(job.plain_buf_idx)), + sizeof (Cbe::Block_data), + nr_of_read_bytes); + + switch (result) { + case Read_result::READ_QUEUED: return; + case Read_result::READ_ERR_INTERRUPT: return; + case Read_result::READ_ERR_AGAIN: return; + case Read_result::READ_ERR_WOULD_BLOCK: return; + default: break; + } + job.request.success(result == Read_result::READ_OK); + job.state = Job_state::COMPLETE; + progress = true; + return; + } + default: + + return; + } +} + + +void Crypto::_execute_encrypt_block(Job &job, + Crypto_plain_buffer &plain_buf, + Crypto_cipher_buffer &cipher_buf, + bool &progress) +{ + switch (job.state) { + case Job_state::SUBMITTED: + { + job.handle->seek(job.request.block_number() * Cbe::BLOCK_SIZE); + file_size nr_of_written_bytes { 0 }; + try { + job.handle->fs().write( + job.handle, + reinterpret_cast( + &plain_buf.item(job.plain_buf_idx)), + file_size(sizeof (Cbe::Block_data)), + nr_of_written_bytes); + + job.state = Job_state::OP_WRITTEN_TO_VFS_HANDLE; + progress = true; + return; + + } catch (Vfs::File_io_service::Insufficient_buffer) { + + return; + } + } + case Job_state::OP_WRITTEN_TO_VFS_HANDLE: + { + job.handle->seek(job.request.block_number() * Cbe::BLOCK_SIZE); + bool success { + job.handle->fs().queue_read( + job.handle, sizeof (Cbe::Block_data)) }; + + if (!success) { + return; + } + job.state = Job_state::READING_VFS_HANDLE_SUCCEEDED; + progress = true; + return; + } + case Job_state::READING_VFS_HANDLE_SUCCEEDED: + { + file_size nr_of_read_bytes { 0 }; + Read_result const result { + job.handle->fs().complete_read( + job.handle, + reinterpret_cast( + &cipher_buf.item(job.cipher_buf_idx)), + sizeof (Cbe::Block_data), + nr_of_read_bytes) }; + + switch (result) { + case Read_result::READ_QUEUED: return; + case Read_result::READ_ERR_INTERRUPT: return; + case Read_result::READ_ERR_AGAIN: return; + case Read_result::READ_ERR_WOULD_BLOCK: return; + default: break; + } + job.request.success(result == Read_result::READ_OK); + job.state = Job_state::COMPLETE; + progress = true; + return; + } + default: + + return; + } +} + + +void Crypto::execute(Crypto_plain_buffer &plain_buf, + Crypto_cipher_buffer &cipher_buf, + bool &progress) +{ + switch (_job.op) { + case Operation::ENCRYPT_BLOCK: + + _execute_encrypt_block(_job, plain_buf, cipher_buf, progress); + break; + + case Operation::DECRYPT_BLOCK: + + _execute_decrypt_block(_job, plain_buf, cipher_buf, progress); + break; + + case Operation::INVALID: + + break; + } +} diff --git a/repos/gems/src/app/cbe_tester/crypto.h b/repos/gems/src/app/cbe_tester/crypto.h new file mode 100644 index 0000000000..0192fbf426 --- /dev/null +++ b/repos/gems/src/app/cbe_tester/crypto.h @@ -0,0 +1,121 @@ +/* + * \brief Implementation of the Crypto module API using the Crypto VFS API + * \author Martin Stein + * \date 2020-10-29 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _CBE_TESTER__CRYPTO_H_ +#define _CBE_TESTER__CRYPTO_H_ + +/* CBE includes */ +#include + +/* CBE tester includes */ +#include + +class Crypto +{ + public: + + enum class Operation + { + INVALID, + DECRYPT_BLOCK, + ENCRYPT_BLOCK + }; + + enum class Result + { + SUCCEEDED, + FAILED, + RETRY_LATER + }; + + private: + + using Read_result = Vfs::File_io_service::Read_result; + using Open_result = Vfs::Directory_service::Open_result; + + struct Key_directory + { + Vfs::Vfs_handle *encrypt_handle { nullptr }; + Vfs::Vfs_handle *decrypt_handle { nullptr }; + Genode::uint32_t key_id { 0 }; + }; + + enum class Job_state + { + SUBMITTED, + OP_WRITTEN_TO_VFS_HANDLE, + READING_VFS_HANDLE_SUCCEEDED, + COMPLETE + }; + + struct Job + { + Cbe::Request request { }; + Vfs::Vfs_handle *handle { nullptr }; + Job_state state { Job_state::SUBMITTED }; + Operation op { Operation::INVALID }; + Cbe::Crypto_cipher_buffer::Index cipher_buf_idx { 0 }; + Cbe::Crypto_plain_buffer::Index plain_buf_idx { 0 }; + }; + + Vfs::Env &_env; + Genode::String<32> const _path; + Vfs::Vfs_handle &_add_key_handle; + Vfs::Vfs_handle &_remove_key_handle; + Vfs_io_response_handler _vfs_io_response_handler; + Key_directory _key_dirs[2] { { }, { } }; + Job _job { }; + + Key_directory &_get_unused_key_dir(); + + Key_directory &_lookup_key_dir(Genode::uint32_t key_id); + + void _execute_decrypt_block(Job &job, + Cbe::Crypto_plain_buffer &plain_buf, + Cbe::Crypto_cipher_buffer &cipher_buf, + bool &progress); + + void _execute_encrypt_block(Job &job, + Cbe::Crypto_plain_buffer &plain_buf, + Cbe::Crypto_cipher_buffer &cipher_buf, + bool &progress); + + public: + + Crypto(Vfs::Env &env, + Genode::Xml_node const &crypto, + Genode::Signal_context_capability sigh); + + bool request_acceptable() const; + + Result add_key(Cbe::Key const &key); + + Result remove_key(Cbe::Key::Id key_id); + + void submit_request(Cbe::Request const &request, + Operation op, + Cbe::Crypto_plain_buffer::Index plain_buf_idx, + Cbe::Crypto_cipher_buffer::Index cipher_buf_idx); + + Cbe::Request peek_completed_encryption_request() const; + + Cbe::Request peek_completed_decryption_request() const; + + void drop_completed_request(); + + void execute(Cbe::Crypto_plain_buffer &plain_buf, + Cbe::Crypto_cipher_buffer &cipher_buf, + bool &progress); +}; + +#endif /* _CBE_TESTER__CRYPTO_H_ */ diff --git a/repos/gems/src/app/cbe_tester/main.cc b/repos/gems/src/app/cbe_tester/main.cc new file mode 100644 index 0000000000..9d1d68aacc --- /dev/null +++ b/repos/gems/src/app/cbe_tester/main.cc @@ -0,0 +1,2561 @@ +/* + * \brief Tool for running tests and benchmarks on the CBE + * \author Martin Stein + * \date 2020-08-26 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include + +/* CBE includes */ +#include +#include +#include +#include +#include +#include + +/* CBE tester includes */ +#include +#include +#include + +using namespace Genode; +using namespace Cbe; +using namespace Vfs; + + +enum class Module_type : uint8_t +{ + CBE_INIT, + CBE_DUMP, + CBE_CHECK, + CBE, + CMD_POOL, +}; + + +static Module_type module_type_from_uint32(uint32_t uint32) +{ + class Bad_tag { }; + switch (uint32) { + case 1: return Module_type::CBE_INIT; + case 2: return Module_type::CBE; + case 3: return Module_type::CBE_DUMP; + case 4: return Module_type::CBE_CHECK; + case 5: return Module_type::CMD_POOL; + default: throw Bad_tag { }; + } +} + + +static uint32_t module_type_to_uint32(Module_type type) +{ + class Bad_type { }; + switch (type) { + case Module_type::CBE_INIT : return 1; + case Module_type::CBE : return 2; + case Module_type::CBE_DUMP : return 3; + case Module_type::CBE_CHECK: return 4; + case Module_type::CMD_POOL : return 5; + } + throw Bad_type { }; +} + + +static Module_type tag_get_module_type(uint32_t tag) +{ + return module_type_from_uint32((tag >> 24) & 0xff); +} + + +static uint32_t tag_set_module_type(uint32_t tag, + Module_type type) +{ + if (tag >> 24) { + + class Bad_tag { }; + throw Bad_tag { }; + } + return tag | (module_type_to_uint32(type) << 24); +} + + +static uint32_t tag_unset_module_type(uint32_t tag) +{ + return tag & 0xffffff; +} + + +static char const *blk_pkt_op_to_string(Block::Packet_descriptor::Opcode op) +{ + switch (op) { + case Block::Packet_descriptor::READ: return "read"; + case Block::Packet_descriptor::WRITE: return "write"; + case Block::Packet_descriptor::SYNC: return "sync"; + case Block::Packet_descriptor::TRIM: return "trim"; + case Block::Packet_descriptor::END: return "end"; + }; + return "?"; +} + + +static String<128> blk_pkt_to_string(Block::Packet_descriptor const &packet) +{ + return + String<128>( + "op=", blk_pkt_op_to_string(packet.operation()), + " vba=", packet.block_number(), + " cnt=", packet.block_count(), + " succ=", packet.succeeded(), + " tag=", Hex(packet.tag().value)); +} + + +template +T read_attribute(Xml_node const &node, + char const *attr) +{ + T value { }; + + if (!node.has_attribute(attr)) { + + error("<", node.type(), "> node misses attribute '", attr, "'"); + class Attribute_missing { }; + throw Attribute_missing { }; + } + if (!node.attribute(attr).value(value)) { + + error("<", node.type(), "> node has malformed '", attr, "' attribute"); + class Malformed_attribute { }; + throw Malformed_attribute { }; + } + return value; +} + + +static void print_blk_data(Block_data const &blk_data) +{ + for(unsigned long idx = 0; idx < Cbe::BLOCK_SIZE; idx += 64) { + log( + " ", idx, ": ", + Hex(blk_data.values[idx + 0], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 1], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 2], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 3], Hex::OMIT_PREFIX, Hex::PAD), " ", + Hex(blk_data.values[idx + 4], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 5], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 6], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 7], Hex::OMIT_PREFIX, Hex::PAD), " ", + Hex(blk_data.values[idx + 8], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 9], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 10], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 11], Hex::OMIT_PREFIX, Hex::PAD), " ", + Hex(blk_data.values[idx + 12], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 13], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 14], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 15], Hex::OMIT_PREFIX, Hex::PAD), " ", + Hex(blk_data.values[idx + 16], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 17], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 18], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 19], Hex::OMIT_PREFIX, Hex::PAD), " ", + Hex(blk_data.values[idx + 20], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 21], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 22], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 23], Hex::OMIT_PREFIX, Hex::PAD), " ", + Hex(blk_data.values[idx + 24], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 25], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 26], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 27], Hex::OMIT_PREFIX, Hex::PAD), " ", + Hex(blk_data.values[idx + 28], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 29], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 30], Hex::OMIT_PREFIX, Hex::PAD), + Hex(blk_data.values[idx + 31], Hex::OMIT_PREFIX, Hex::PAD)); + } +} + + +class Block_io : public Interface +{ + public: + + virtual bool request_acceptable() = 0; + + virtual void submit_request(Cbe::Request const &cbe_req, + Block_data &data) = 0; + + virtual void execute(Constructible &cbe, + Cbe_init::Library &cbe_init, + Cbe_dump::Library &cbe_dump, + Cbe_check::Library &cbe_check, + Verbose_node const &verbose_node, + Io_buffer &blk_buf, + bool &progress) = 0; +}; + + +class Vfs_block_io_job +{ + private: + + using file_size = Vfs::file_size; + using file_offset = Vfs::file_offset; + + enum State { PENDING, IN_PROGRESS, COMPLETE }; + + Vfs::Vfs_handle &_handle; + Cbe::Request _cbe_req; + State _state; + file_offset _nr_of_processed_bytes; + file_size _nr_of_remaining_bytes; + + Cbe::Io_buffer::Index _cbe_req_io_buf_idx(Cbe::Request const &cbe_req) + { + return + Cbe::Io_buffer::Index { + (uint32_t)cbe_req.tag() & 0xffffff }; + } + + void + _mark_req_completed_at_module(Constructible &cbe, + Cbe_init::Library &cbe_init, + Cbe_dump::Library &cbe_dump, + Cbe_check::Library &cbe_check, + Verbose_node const &verbose_node, + bool &progress) + { + Cbe::Io_buffer::Index const data_index { + _cbe_req_io_buf_idx(_cbe_req) }; + + switch (tag_get_module_type(_cbe_req.tag())) { + case Module_type::CBE_INIT: + + cbe_init.io_request_completed(data_index, _cbe_req.success()); + break; + + case Module_type::CBE: + + cbe->io_request_completed(data_index, _cbe_req.success()); + break; + + case Module_type::CBE_DUMP: + + cbe_dump.io_request_completed(data_index, _cbe_req.success()); + break; + + case Module_type::CBE_CHECK: + + cbe_check.io_request_completed(data_index, _cbe_req.success()); + break; + + case Module_type::CMD_POOL: + + class Bad_module_type { }; + throw Bad_module_type { }; + } + progress = true; + + if (verbose_node.blk_io_req_completed()) { + log("blk req completed: ", _cbe_req); + } + } + + + void _execute_read(Constructible &cbe, + Cbe_init::Library &cbe_init, + Cbe_dump::Library &cbe_dump, + Cbe_check::Library &cbe_check, + Verbose_node const &verbose_node, + Io_buffer &io_data, + bool &progress) + { + using Result = Vfs::File_io_service::Read_result; + + switch (_state) { + case State::PENDING: + + _handle.seek(_cbe_req.block_number() * Cbe::BLOCK_SIZE + + _nr_of_processed_bytes); + + if (!_handle.fs().queue_read(&_handle, _nr_of_remaining_bytes)) { + return; + } + _state = State::IN_PROGRESS; + progress = true; + return; + + case State::IN_PROGRESS: + { + file_size nr_of_read_bytes { 0 }; + + char *const data { + reinterpret_cast( + &io_data.item(_cbe_req_io_buf_idx(_cbe_req))) }; + + Result const result { + _handle.fs().complete_read(&_handle, + data + _nr_of_processed_bytes, + _nr_of_remaining_bytes, + nr_of_read_bytes) }; + + switch (result) { + case Result::READ_QUEUED: + case Result::READ_ERR_INTERRUPT: + case Result::READ_ERR_AGAIN: + case Result::READ_ERR_WOULD_BLOCK: + + return; + + case Result::READ_OK: + + _nr_of_processed_bytes += nr_of_read_bytes; + _nr_of_remaining_bytes -= nr_of_read_bytes; + + if (_nr_of_remaining_bytes == 0) { + + _state = State::COMPLETE; + _cbe_req.success(true); + + _mark_req_completed_at_module( + cbe, cbe_init, cbe_dump, cbe_check, + verbose_node, progress); + + progress = true; + return; + + } else { + + _state = State::PENDING; + progress = true; + return; + } + + case Result::READ_ERR_IO: + case Result::READ_ERR_INVALID: + + _state = State::COMPLETE; + _cbe_req.success(false); + + _mark_req_completed_at_module( + cbe, cbe_init, cbe_dump, cbe_check, + verbose_node, progress); + + progress = true; + return; + + default: + + class Bad_complete_read_result { }; + throw Bad_complete_read_result { }; + } + } + default: return; + } + } + + void _execute_write(Constructible &cbe, + Cbe_init::Library &cbe_init, + Cbe_dump::Library &cbe_dump, + Cbe_check::Library &cbe_check, + Verbose_node const &verbose_node, + Io_buffer &io_data, + bool &progress) + { + using Result = Vfs::File_io_service::Write_result; + + switch (_state) { + case State::PENDING: + + _handle.seek(_cbe_req.block_number() * Cbe::BLOCK_SIZE + + _nr_of_processed_bytes); + + _state = State::IN_PROGRESS; + progress = true; + break; + + case State::IN_PROGRESS: + { + file_size nr_of_written_bytes { 0 }; + + char const *const data { + reinterpret_cast( + &io_data.item(_cbe_req_io_buf_idx(_cbe_req))) }; + + Result result; + try { + result = _handle.fs().write(&_handle, + data + _nr_of_processed_bytes, + _nr_of_remaining_bytes, + nr_of_written_bytes); + + } catch (Vfs::File_io_service::Insufficient_buffer) { + + return; + } + switch (result) { + case Result::WRITE_ERR_AGAIN: + case Result::WRITE_ERR_INTERRUPT: + case Result::WRITE_ERR_WOULD_BLOCK: + + return; + + case Result::WRITE_OK: + + _nr_of_processed_bytes += nr_of_written_bytes; + _nr_of_remaining_bytes -= nr_of_written_bytes; + + if (_nr_of_remaining_bytes == 0) { + + _state = State::COMPLETE; + _cbe_req.success(true); + + _mark_req_completed_at_module( + cbe, cbe_init, cbe_dump, cbe_check, + verbose_node, progress); + + progress = true; + return; + + } else { + + _state = State::PENDING; + progress = true; + return; + } + + case Result::WRITE_ERR_IO: + case Result::WRITE_ERR_INVALID: + + _state = State::COMPLETE; + _cbe_req.success(false); + + _mark_req_completed_at_module( + cbe, cbe_init, cbe_dump, cbe_check, + verbose_node, progress); + + progress = true; + return; + + default: + + class Bad_write_result { }; + throw Bad_write_result { }; + } + + } + default: return; + } + } + + void _execute_sync(Constructible &cbe, + Cbe_init::Library &cbe_init, + Cbe_dump::Library &cbe_dump, + Cbe_check::Library &cbe_check, + Verbose_node const &verbose_node, + bool &progress) + { + using Result = Vfs::File_io_service::Sync_result; + + switch (_state) { + case State::PENDING: + + if (!_handle.fs().queue_sync(&_handle)) { + return; + } + _state = State::IN_PROGRESS; + progress = true; + break;; + + case State::IN_PROGRESS: + + switch (_handle.fs().complete_sync(&_handle)) { + case Result::SYNC_QUEUED: + + return; + + case Result::SYNC_ERR_INVALID: + + _cbe_req.success(false); + _mark_req_completed_at_module( + cbe, cbe_init, cbe_dump, cbe_check, verbose_node, + progress); + + _state = State::COMPLETE; + progress = true; + return; + + case Result::SYNC_OK: + + _cbe_req.success(true); + _mark_req_completed_at_module( + cbe, cbe_init, cbe_dump, cbe_check, verbose_node, + progress); + + _state = State::COMPLETE; + progress = true; + return; + + default: + + class Bad_sync_result { }; + throw Bad_sync_result { }; + } + + default: return; + } + } + + public: + + Vfs_block_io_job(Vfs::Vfs_handle &handle, + Cbe::Request cbe_req) + : + _handle { handle }, + _cbe_req { cbe_req }, + _state { State::PENDING }, + _nr_of_processed_bytes { 0 }, + _nr_of_remaining_bytes { _cbe_req.count() * Cbe::BLOCK_SIZE } + { } + + bool complete() const + { + return _state == COMPLETE; + } + + void execute(Constructible &cbe, + Cbe_init::Library &cbe_init, + Cbe_dump::Library &cbe_dump, + Cbe_check::Library &cbe_check, + Verbose_node const &verbose_node, + Io_buffer &blk_buf, + bool &progress) + { + using Cbe_operation = Cbe::Request::Operation; + + switch (_cbe_req.operation()) { + case Cbe_operation::READ: + + _execute_read(cbe, cbe_init, cbe_dump, cbe_check, verbose_node, + blk_buf, progress); + + break; + + case Cbe_operation::WRITE: + + _execute_write(cbe, cbe_init, cbe_dump, cbe_check, verbose_node, + blk_buf, progress); + + break; + + case Cbe_operation::SYNC: + + _execute_sync(cbe, cbe_init, cbe_dump, cbe_check, verbose_node, + progress); + + break; + + default: + + class Bad_cbe_operation { }; + throw Bad_cbe_operation { }; + } + } +}; + + +class Vfs_block_io : public Block_io +{ + private: + + String<32> const _path; + Vfs::Env &_vfs_env; + Vfs_io_response_handler _vfs_io_response_handler; + Vfs::Vfs_handle &_vfs_handle { *_init_vfs_handle(_vfs_env, _path) }; + Constructible _job { }; + + Vfs_block_io(const Vfs_block_io&); + + const Vfs_block_io& operator=(const Vfs_block_io&); + + static Vfs::Vfs_handle *_init_vfs_handle(Vfs::Env &vfs_env, + String<32> const &path) + { + using Result = Vfs::Directory_service::Open_result; + + Vfs::Vfs_handle *vfs_handle { nullptr }; + Result const result { + vfs_env.root_dir().open( + path.string(), Vfs::Directory_service::OPEN_MODE_RDWR, + &vfs_handle, vfs_env.alloc()) }; + + if (result != Result::OPEN_OK) { + + class Open_failed { }; + throw Open_failed { }; + } + return vfs_handle; + } + + public: + + Vfs_block_io(Vfs::Env &vfs_env, + Xml_node const &block_io, + Signal_context_capability sigh) + : + _path { block_io.attribute_value( + "path", String<32> { "" } ) }, + + _vfs_env { vfs_env }, + _vfs_io_response_handler { sigh } + { + _vfs_handle.handler(&_vfs_io_response_handler); + } + + + /************** + ** Block_io ** + **************/ + + bool request_acceptable() override + { + return !_job.constructed(); + } + + void submit_request(Cbe::Request const &cbe_req, + Block_data &) override + { + _job.construct(_vfs_handle, cbe_req); + } + + void execute(Constructible &cbe, + Cbe_init::Library &cbe_init, + Cbe_dump::Library &cbe_dump, + Cbe_check::Library &cbe_check, + Verbose_node const &verbose_node, + Io_buffer &blk_buf, + bool &progress) override + { + if (!_job.constructed()) { + return; + } + _job->execute( + cbe, cbe_init, cbe_dump, cbe_check, verbose_node, blk_buf, + progress); + + if (_job->complete()) { + _job.destruct(); + } + } +}; + + +class Block_connection_block_io : public Block_io +{ + private: + + enum { TX_BUF_SIZE = Block::Session::TX_QUEUE_SIZE * BLOCK_SIZE, }; + + Genode::Env &_env; + Heap &_heap; + Allocator_avl _blk_alloc { &_heap }; + Block::Connection<> _blk { _env, &_blk_alloc, TX_BUF_SIZE }; + + Cbe::Io_buffer::Index _packet_io_buf_idx(Block::Packet_descriptor const & pkt) + { + return + Cbe::Io_buffer::Index { + (uint32_t)pkt.tag().value & 0xffffff }; + } + + static Block::Packet_descriptor::Opcode + _cbe_op_to_block_op(Cbe::Request::Operation cbe_op) + { + switch (cbe_op) { + case Cbe::Request::Operation::READ: return Block::Packet_descriptor::READ; + case Cbe::Request::Operation::WRITE: return Block::Packet_descriptor::WRITE; + case Cbe::Request::Operation::SYNC: return Block::Packet_descriptor::SYNC; + default: + class Bad_cbe_op { }; + throw Bad_cbe_op { }; + } + } + + public: + + Block_connection_block_io(Genode::Env &env, + Heap &heap, + Signal_context_capability sigh) + : + _env { env }, + _heap { heap } + { + _blk.tx_channel()->sigh_ack_avail(sigh); + _blk.tx_channel()->sigh_ready_to_submit(sigh); + } + + ~Block_connection_block_io() + { + _blk.tx_channel()->sigh_ack_avail(Signal_context_capability()); + _blk.tx_channel()->sigh_ready_to_submit(Signal_context_capability()); + } + + + /************** + ** Block_io ** + **************/ + + bool request_acceptable() override + { + return _blk.tx()->ready_to_submit(); + } + + void submit_request(Cbe::Request const &cbe_req, + Block_data &data) override + { + Block::Packet_descriptor::Opcode const blk_op { + _cbe_op_to_block_op(cbe_req.operation()) }; + + Block::Packet_descriptor const packet { + _blk.alloc_packet(Cbe::BLOCK_SIZE), + blk_op, + cbe_req.block_number(), + cbe_req.count(), + Block::Packet_descriptor::Tag { cbe_req.tag() } }; + if (cbe_req.operation() == Cbe::Request::Operation::WRITE) { + + *reinterpret_cast( + _blk.tx()->packet_content(packet)) = data; + } + _blk.tx()->try_submit_packet(packet); + } + + void execute(Constructible &cbe, + Cbe_init::Library &cbe_init, + Cbe_dump::Library &cbe_dump, + Cbe_check::Library &cbe_check, + Verbose_node const &verbose_node, + Io_buffer &blk_buf, + bool &progress) override + { + while (_blk.tx()->ack_avail()) { + + Block::Packet_descriptor packet { + _blk.tx()->try_get_acked_packet() }; + + Cbe::Io_buffer::Index const data_index { + _packet_io_buf_idx(packet) }; + + if (packet.operation() == Block::Packet_descriptor::READ && + packet.succeeded()) + { + blk_buf.item(data_index) = + *reinterpret_cast( + _blk.tx()->packet_content(packet)); + } + switch (tag_get_module_type(packet.tag().value)) { + case Module_type::CBE_INIT: + + cbe_init.io_request_completed(data_index, + packet.succeeded()); + break; + + case Module_type::CBE: + + cbe->io_request_completed(data_index, + packet.succeeded()); + break; + + case Module_type::CBE_DUMP: + + cbe_dump.io_request_completed(data_index, + packet.succeeded()); + break; + + case Module_type::CBE_CHECK: + + cbe_check.io_request_completed(data_index, + packet.succeeded()); + break; + + case Module_type::CMD_POOL: + + class Bad_module_type { }; + throw Bad_module_type { }; + } + _blk.tx()->release_packet(packet); + progress = true; + + if (verbose_node.blk_io_req_completed()) { + log("blk pkt completed: ", blk_pkt_to_string(packet)); + } + } + _blk.tx()->wakeup(); + } +}; + + +class Log_node +{ + private: + + String<128> const _string; + + public: + + Log_node(Xml_node const &node) + : + _string { node.attribute_value("string", String<128> { }) } + { } + + String<128> const &string() const { return _string; } + + void print(Genode::Output &out) const + { + Genode::print(out, "string=\"", _string, "\""); + } +}; + + +class Benchmark_node +{ + public: + + using Label = String<128>; + + enum Operation { START, STOP }; + + private: + + Operation const _op; + bool const _label_avail; + Label const _label; + + Operation _read_op_attr(Xml_node const &node) + { + class Attribute_missing { }; + if (!node.has_attribute("op")) { + throw Attribute_missing { }; + } + if (node.attribute("op").has_value("start")) { + return Operation::START; + } + if (node.attribute("op").has_value("stop")) { + return Operation::STOP; + } + class Malformed_attribute { }; + throw Malformed_attribute { }; + } + + static char const *_op_to_string(Operation op) + { + switch (op) { + case START: return "start"; + case STOP: return "stop"; + } + return "?"; + } + + public: + + bool has_attr_label() const + { + return _op == Operation::START; + } + + Benchmark_node(Xml_node const &node) + : + _op { _read_op_attr(node) }, + _label_avail { has_attr_label() && node.has_attribute("label") }, + _label { _label_avail ? + node.attribute_value("label", Label { }) : + Label { } } + { } + + Operation op() const { return _op; } + bool label_avail() const { return _label_avail; } + Label const &label() const { return _label; } + + void print(Genode::Output &out) const + { + Genode::print(out, "op=", _op_to_string(_op)); + if (_label_avail) { + Genode::print(out, " label=", _label); + } + } +}; + + +class Benchmark +{ + private: + + enum State { STARTED, STOPPED }; + + Genode::Env &_env; + Timer::Connection _timer { _env }; + State _state { STOPPED }; + Microseconds _start_time { 0 }; + uint64_t _nr_of_virt_blks_read { 0 }; + uint64_t _nr_of_virt_blks_written { 0 }; + Constructible _start_node { }; + uint64_t _id { 0 }; + + public: + + Benchmark(Genode::Env &env) : _env { env } { } + + void submit_request(Benchmark_node const &node) + { + switch (node.op()) { + case Benchmark_node::START: + + if (_state != STOPPED) { + class Bad_state_to_start { }; + throw Bad_state_to_start { }; + } + _id++; + _nr_of_virt_blks_read = 0; + _nr_of_virt_blks_written = 0; + _state = STARTED; + _start_node.construct(node); + _start_time = _timer.curr_time().trunc_to_plain_us(); + break; + + case Benchmark_node::STOP: + + if (_state != STARTED) { + class Bad_state_to_stop { }; + throw Bad_state_to_stop { }; + } + uint64_t const stop_time_us { + _timer.curr_time().trunc_to_plain_us().value }; + + log(""); + if (_start_node->label_avail()) { + log("Benchmark result \"", _start_node->label(), "\""); + } else { + log("Benchmark result (command ID ", _id, ")"); + } + + double const passed_time_sec { + (double)(stop_time_us - _start_time.value) / + (double)(1000 * 1000) }; + + log(" Ran ", passed_time_sec, " seconds."); + + if (_nr_of_virt_blks_read != 0) { + + uint64_t const bytes_read { + _nr_of_virt_blks_read * Cbe::BLOCK_SIZE }; + + double const mibyte_read { + (double)bytes_read / (double)(1024 * 1024) }; + + double const mibyte_per_sec_read { + (double)bytes_read / (double)passed_time_sec / + (double)(1024 * 1024) }; + + log(" Have read ", mibyte_read, " mebibyte in total."); + log(" Have read ", mibyte_per_sec_read, " mebibyte per second."); + } + + if (_nr_of_virt_blks_written != 0) { + + uint64_t bytes_written { + _nr_of_virt_blks_written * Cbe::BLOCK_SIZE }; + + double const mibyte_written { + (double)bytes_written / (double)(1024 * 1024) }; + + double const mibyte_per_sec_written { + (double)bytes_written / (double)passed_time_sec / + (double)(1024 * 1024) }; + + log(" Have written ", mibyte_written, " mebibyte in total."); + log(" Have written ", mibyte_per_sec_written, " mebibyte per second."); + } + log(""); + _state = STOPPED; + break; + } + } + + void raise_nr_of_virt_blks_read() { _nr_of_virt_blks_read++; } + void raise_nr_of_virt_blks_written() { _nr_of_virt_blks_written++; } +}; + + +class Trust_anchor_node +{ + private: + + using Operation = Trust_anchor_request::Operation; + + Operation const _op; + String<64> const _passphrase; + + Operation _read_op_attr(Xml_node const &node) + { + class Attribute_missing { }; + if (!node.has_attribute("op")) { + throw Attribute_missing { }; + } + if (node.attribute("op").has_value("initialize")) { + return Operation::INITIALIZE; + } + class Malformed_attribute { }; + throw Malformed_attribute { }; + } + + public: + + Trust_anchor_node(Xml_node const &node) + : + _op { _read_op_attr(node) }, + _passphrase { has_attr_passphrase() ? + node.attribute_value("passphrase", String<64>()) : + String<64>() } + { } + + Operation op() const { return _op; } + String<64> const &passphrase() const { return _passphrase; } + + bool has_attr_passphrase() const + { + return _op == Operation::INITIALIZE; + } + + void print(Genode::Output &out) const + { + Genode::print(out, "op=", to_string(_op)); + if (has_attr_passphrase()) { + Genode::print(out, " passphrase=", _passphrase); + } + } +}; + + +class Request_node +{ + private: + + using Operation = Cbe::Request::Operation; + + Operation const _op; + Virtual_block_address const _vba; + Number_of_blocks const _count; + bool const _sync; + bool const _salt_avail; + uint64_t const _salt; + + Operation _read_op_attr(Xml_node const &node) + { + class Attribute_missing { }; + if (!node.has_attribute("op")) { + throw Attribute_missing { }; + } + if (node.attribute("op").has_value("read")) { + return Operation::READ; + } + if (node.attribute("op").has_value("write")) { + return Operation::WRITE; + } + if (node.attribute("op").has_value("sync")) { + return Operation::SYNC; + } + if (node.attribute("op").has_value("create_snapshot")) { + return Operation::CREATE_SNAPSHOT; + } + if (node.attribute("op").has_value("extend_ft")) { + return Operation::EXTEND_FT; + } + if (node.attribute("op").has_value("extend_vbd")) { + return Operation::EXTEND_VBD; + } + if (node.attribute("op").has_value("rekey")) { + return Operation::REKEY; + } + if (node.attribute("op").has_value("deinitialize")) { + return Operation::DEINITIALIZE; + } + class Malformed_attribute { }; + throw Malformed_attribute { }; + } + + public: + + Request_node(Xml_node const &node) + : + _op { _read_op_attr(node) }, + _vba { has_attr_vba() ? + read_attribute(node, "vba") : 0 }, + _count { has_attr_count() ? + read_attribute(node, "count") : 0 }, + _sync { read_attribute(node, "sync") }, + _salt_avail { has_attr_salt() ? + node.has_attribute("salt") : false }, + _salt { has_attr_salt() && _salt_avail ? + read_attribute(node, "salt") : 0 } + { } + + Operation op() const { return _op; } + Virtual_block_address vba() const { return _vba; } + Number_of_blocks count() const { return _count; } + bool sync() const { return _sync; } + bool salt_avail() const { return _salt_avail; } + uint64_t salt() const { return _salt; } + + bool has_attr_vba() const + { + return _op == Operation::READ || + _op == Operation::WRITE || + _op == Operation::SYNC; + } + + bool has_attr_salt() const + { + return _op == Operation::READ || + _op == Operation::WRITE; + } + + bool has_attr_count() const + { + return _op == Operation::READ || + _op == Operation::WRITE || + _op == Operation::SYNC || + _op == Operation::EXTEND_FT || + _op == Operation::EXTEND_VBD; + } + + void print(Genode::Output &out) const + { + Genode::print(out, "op=", to_string(_op)); + if (has_attr_vba()) { + Genode::print(out, " vba=", _vba); + } + if (has_attr_count()) { + Genode::print(out, " count=", _count); + } + Genode::print(out, " sync=", _sync); + if (_salt_avail) { + Genode::print(out, " salt=", _salt); + } + } +}; + + +class Command : public Fifo::Element +{ + public: + + enum Type + { + INVALID, + REQUEST, + TRUST_ANCHOR, + BENCHMARK, + CONSTRUCT, + DESTRUCT, + INITIALIZE, + CHECK, + DUMP, + LIST_SNAPSHOTS, + LOG + }; + + enum State + { + PENDING, + IN_PROGRESS, + COMPLETED + }; + + private: + + Type _type { INVALID }; + uint32_t _id { 0 }; + State _state { PENDING }; + bool _success { false }; + bool _data_mismatch { false }; + Constructible _request_node { }; + Constructible _trust_anchor_node { }; + Constructible _benchmark_node { }; + Constructible _log_node { }; + Constructible _initialize { }; + Constructible _dump { }; + + char const *_state_to_string() const + { + switch (_state) { + case PENDING: return "pending"; + case IN_PROGRESS: return "in_progress"; + case COMPLETED: return "completed"; + } + return "?"; + } + + char const *_type_to_string() const + { + switch (_type) { + case INITIALIZE: return "initialize"; + case INVALID: return "invalid"; + case DUMP: return "dump"; + case REQUEST: return "request"; + case TRUST_ANCHOR: return "trust_anchor"; + case BENCHMARK: return "benchmark"; + case CONSTRUCT: return "construct"; + case DESTRUCT: return "destruct"; + case CHECK: return "check"; + case LIST_SNAPSHOTS: return "list_snapshots"; + case LOG: return "log"; + } + return "?"; + } + + public: + + Command() { } + + Command(Type type, + Xml_node const &node, + uint32_t id) + : + _type { type }, + _id { id } + { + switch (_type) { + case INITIALIZE: _initialize.construct(node); break; + case DUMP: _dump.construct(node); break; + case REQUEST: _request_node.construct(node); break; + case TRUST_ANCHOR: _trust_anchor_node.construct(node); break; + case BENCHMARK: _benchmark_node.construct(node); break; + case LOG: _log_node.construct(node); break; + default: break; + } + } + + Command(Command &other) + : + _type { other._type }, + _id { other._id }, + _state { other._state }, + _success { other._success } + { + switch (_type) { + case INITIALIZE: _initialize.construct(*other._initialize); break; + case DUMP: _dump.construct(*other._dump); break; + case REQUEST: _request_node.construct(*other._request_node); break; + case TRUST_ANCHOR: _trust_anchor_node.construct(*other._trust_anchor_node); break; + case BENCHMARK: _benchmark_node.construct(*other._benchmark_node); break; + case LOG: _log_node.construct(*other._log_node); break; + default: break; + } + } + + bool has_attr_data_mismatch() const + { + return + _type == REQUEST && + _request_node->op() == Cbe::Request::Operation::READ && + _request_node->salt_avail(); + } + + bool synchronize() const + { + class Bad_type { }; + switch (_type) { + case INITIALIZE: return true; + case BENCHMARK: return true; + case CONSTRUCT: return true; + case DESTRUCT: return true; + case DUMP: return true; + case CHECK: return true; + case TRUST_ANCHOR: return true; + case LIST_SNAPSHOTS: return true; + case LOG: return true; + case REQUEST: return _request_node->sync(); + case INVALID: throw Bad_type { }; + } + throw Bad_type { }; + } + + static Type type_from_string(String<64> str) + { + if (str == "initialize") { return INITIALIZE; } + if (str == "request") { return REQUEST; } + if (str == "trust-anchor") { return TRUST_ANCHOR; } + if (str == "benchmark") { return BENCHMARK; } + if (str == "construct") { return CONSTRUCT; } + if (str == "destruct") { return DESTRUCT; } + if (str == "check") { return CHECK; } + if (str == "dump") { return DUMP; } + if (str == "list-snapshots") { return LIST_SNAPSHOTS; } + if (str == "log") { return LOG; } + class Bad_string { }; + throw Bad_string { }; + } + + void print(Genode::Output &out) const + { + Genode::print(out, "id=", _id, " type=", _type_to_string()); + class Bad_type { }; + switch (_type) { + case INITIALIZE: Genode::print(out, " cfg=(", *_initialize, ")"); break; + case REQUEST: Genode::print(out, " cfg=(", *_request_node, ")"); break; + case TRUST_ANCHOR: Genode::print(out, " cfg=(", *_trust_anchor_node, ")"); break; + case BENCHMARK: Genode::print(out, " cfg=(", *_benchmark_node, ")"); break; + case DUMP: Genode::print(out, " cfg=(", *_dump, ")"); break; + case LOG: Genode::print(out, " cfg=(", *_log_node, ")"); break; + case INVALID: break; + case CHECK: break; + case CONSTRUCT: break; + case DESTRUCT: break; + case LIST_SNAPSHOTS: break; + } + Genode::print(out, " succ=", _success); + if (has_attr_data_mismatch()) { + Genode::print(out, " bad_data=", _data_mismatch); + } + Genode::print(out, " state=", _state_to_string()); + } + + Type type () const { return _type ; } + State state () const { return _state ; } + uint32_t id () const { return _id ; } + bool success () const { return _success ; } + bool data_mismatch () const { return _data_mismatch ; } + Request_node const &request_node () const { return *_request_node ; } + Trust_anchor_node const &trust_anchor_node () const { return *_trust_anchor_node; } + Benchmark_node const &benchmark_node () const { return *_benchmark_node ; } + Log_node const &log_node () const { return *_log_node ; } + Cbe_init::Configuration const &initialize () const { return *_initialize ; } + Cbe_dump::Configuration const &dump () const { return *_dump ; } + + void state (State state) { _state = state; } + void success (bool success) { _success = success; } + void data_mismatch (bool data_mismatch) { _data_mismatch = data_mismatch; } + +}; + + +class Command_pool { + + private: + + Allocator &_alloc; + Verbose_node const &_verbose_node; + Fifo _cmd_queue { }; + uint32_t _next_command_id { 0 }; + unsigned long _nr_of_uncompleted_cmds { 0 }; + unsigned long _nr_of_errors { 0 }; + Block_data _blk_data { }; + + void _read_cmd_node(Xml_node const &node, + Command::Type cmd_type) + { + Command &cmd { + *new (_alloc) Command(cmd_type, node, _next_command_id++) }; + + _nr_of_uncompleted_cmds++; + _cmd_queue.enqueue(cmd); + + if (_verbose_node.cmd_pool_cmd_pending()) { + log("cmd pending: ", cmd); + } + } + + static void _generate_blk_data(Block_data &blk_data, + Virtual_block_address vba, + uint64_t salt) + { + for (uint64_t idx { 0 }; + idx + sizeof(vba) + sizeof(salt) <= + sizeof(blk_data.values) / sizeof(blk_data.values[0]); ) + { + memcpy(&blk_data.values[idx], &vba, sizeof(vba)); + idx += sizeof(vba); + memcpy(&blk_data.values[idx], &salt, sizeof(salt)); + idx += sizeof(salt); + vba += idx + salt; + salt += idx + vba; + } + } + + public: + + Command_pool(Allocator &alloc, + Xml_node const &config_xml, + Verbose_node const &verbose_node) + : + _alloc { alloc }, + _verbose_node { verbose_node } + { + config_xml.sub_node("commands").for_each_sub_node( + [&] (Xml_node const &node) + { + _read_cmd_node(node, Command::type_from_string(node.type())); + }); + } + + Command peek_pending_command(Command::Type type) const + { + Reconstructible resulting_cmd { }; + bool first_uncompleted_cmd { true }; + bool exit_loop { false }; + _cmd_queue.for_each([&] (Command &curr_cmd) + { + if (exit_loop) { + return; + } + switch (curr_cmd.state()) { + case Command::PENDING: + + /* + * Stop iterating at the first uncompleted command + * that needs to be synchronized. + */ + if (curr_cmd.synchronize()) { + if (curr_cmd.type() == type && first_uncompleted_cmd) { + resulting_cmd.construct(curr_cmd); + } + exit_loop = true; + return; + } + /* + * Select command and stop iterating if the command is of + * the desired type. + */ + if (curr_cmd.type() == type) { + resulting_cmd.construct(curr_cmd); + exit_loop = true; + } + first_uncompleted_cmd = false; + return; + + case Command::IN_PROGRESS: + + /* + * Stop iterating at the first uncompleted command + * that needs to be synchronized. + */ + if (curr_cmd.synchronize()) { + exit_loop = true; + return; + } + first_uncompleted_cmd = false; + return; + + case Command::COMPLETED: + + return; + } + }); + return *resulting_cmd; + } + + void mark_command_in_progress(unsigned long cmd_id) + { + bool exit_loop { false }; + _cmd_queue.for_each([&] (Command &cmd) + { + if (exit_loop) { + return; + } + if (cmd.id() == cmd_id) { + if (cmd.state() != Command::PENDING) { + class Bad_state { }; + throw Bad_state { }; + } + cmd.state(Command::IN_PROGRESS); + exit_loop = true; + + if (_verbose_node.cmd_pool_cmd_in_progress()) { + log("cmd in progress: ", cmd); + } + } + }); + } + + void mark_command_completed(unsigned long cmd_id, + bool success) + { + bool exit_loop { false }; + _cmd_queue.for_each([&] (Command &cmd) + { + if (exit_loop) { + return; + } + if (cmd.id() == cmd_id) { + + if (cmd.state() != Command::IN_PROGRESS) { + + class Bad_state { }; + throw Bad_state { }; + } + cmd.state(Command::COMPLETED); + _nr_of_uncompleted_cmds--; + cmd.success(success); + if (!cmd.success()) { + _nr_of_errors++; + } + exit_loop = true; + + if (_verbose_node.cmd_pool_cmd_completed()) { + log("cmd completed: ", cmd); + } + } + }); + } + + void generate_blk_data(Cbe::Request cbe_req, + Virtual_block_address vba, + Block_data &blk_data) const + { + bool exit_loop { false }; + _cmd_queue.for_each([&] (Command &cmd) + { + if (exit_loop) { + return; + } + if (cmd.id() != cbe_req.tag()) { + return; + } + if (cmd.type() != Command::REQUEST) { + class Bad_command_type { }; + throw Bad_command_type { }; + } + Request_node const &req_node { cmd.request_node() }; + if (req_node.salt_avail()) { + + _generate_blk_data(blk_data, vba, req_node.salt()); + } + exit_loop = true; + }); + } + + void verify_blk_data(Cbe::Request cbe_req, + Virtual_block_address vba, + Block_data const &blk_data) + { + bool exit_loop { false }; + _cmd_queue.for_each([&] (Command &cmd) + { + if (exit_loop) { + return; + } + if (cmd.id() != cbe_req.tag()) { + return; + } + if (cmd.type() != Command::REQUEST) { + class Bad_command_type { }; + throw Bad_command_type { }; + } + Request_node const &req_node { cmd.request_node() }; + if (req_node.salt_avail()) { + + Block_data gen_blk_data { }; + _generate_blk_data(gen_blk_data, vba, req_node.salt()); + + if (memcmp(blk_data.values, gen_blk_data.values, + sizeof(blk_data.values) / + sizeof(blk_data.values[0]))) { + + cmd.data_mismatch(true); + _nr_of_errors++; + + if (_verbose_node.client_data_mismatch()) { + log("client data mismatch: vba=", vba, + " req=(", cbe_req, ")"); + log("client data should be:"); + print_blk_data(gen_blk_data); + log("client data is:"); + print_blk_data(blk_data); + class Client_data_mismatch { }; + throw Client_data_mismatch { }; + } + } + } + exit_loop = true; + }); + } + + void print_failed_cmds() const + { + _cmd_queue.for_each([&] (Command &cmd) + { + if (cmd.state() != Command::COMPLETED) { + return; + } + if (cmd.success() && + (!cmd.has_attr_data_mismatch() || !cmd.data_mismatch())) { + + return; + } + log("cmd failed: ", cmd); + }); + } + + unsigned long nr_of_uncompleted_cmds() { return _nr_of_uncompleted_cmds; } + unsigned long nr_of_errors() { return _nr_of_errors; } +}; + + +class Main +{ + private: + + Genode::Env &_env; + Attached_rom_dataspace _config_rom { _env, "config" }; + Verbose_node _verbose_node { _config_rom.xml() }; + Heap _heap { _env.ram(), _env.rm() }; + Vfs::Simple_env _vfs_env { _env, _heap, _config_rom.xml().sub_node("vfs") }; + Signal_handler
_sigh { _env.ep(), *this, &Main::_execute }; + Block_io &_blk_io { _init_blk_io(_config_rom.xml(), _heap, _env, _vfs_env, _sigh) }; + Io_buffer _blk_buf { }; + Command_pool _cmd_pool { _heap, _config_rom.xml(), _verbose_node }; + Constructible _cbe { }; + Cbe_check::Library _cbe_check { }; + Cbe_dump::Library _cbe_dump { }; + Cbe_init::Library _cbe_init { }; + Benchmark _benchmark { _env }; + Trust_anchor _trust_anchor { _vfs_env, _config_rom.xml().sub_node("trust-anchor"), _sigh }; + Crypto_plain_buffer _crypto_plain_buf { }; + Crypto_cipher_buffer _crypto_cipher_buf { }; + Crypto _crypto { _vfs_env, + _config_rom.xml().sub_node("crypto"), + _sigh }; + + Block_io &_init_blk_io(Xml_node const &config, + Heap &heap, + Genode::Env &env, + Vfs::Env &vfs_env, + Signal_context_capability sigh) + { + Xml_node const &block_io { config.sub_node("block-io") }; + if (block_io.attribute("type").has_value("block_connection")) { + return *new (heap) + Block_connection_block_io { env, heap, sigh }; + } + if (block_io.attribute("type").has_value("vfs")) { + return *new (heap) + Vfs_block_io { + vfs_env, block_io, sigh }; + } + class Malformed_attribute { }; + throw Malformed_attribute { }; + } + + template + void _handle_pending_blk_io_requests_of_module(MODULE &module, + Module_type module_type, + bool &progress) + { + while (true) { + + if (!_blk_io.request_acceptable()) { + break; + } + Cbe::Io_buffer::Index data_index { 0 }; + Cbe::Request cbe_req { }; + module.has_io_request(cbe_req, data_index); + if (!cbe_req.valid()) { + break; + } + if (data_index.value & 0xff000000) { + class Bad_data_index { }; + throw Bad_data_index { }; + } + cbe_req.tag( + tag_set_module_type(data_index.value, module_type)); + + _blk_io.submit_request(cbe_req, _blk_buf.item(data_index)); + + if (_verbose_node.blk_io_req_in_progress()) { + log("blk req in progress: ", cbe_req); + } + module.io_request_in_progress(data_index); + progress = true; + } + } + + template + void _handle_completed_client_requests_of_module(MODULE &module, + bool &progress) + { + while (true) { + + Cbe::Request const cbe_req { + module.peek_completed_client_request() }; + + if (!cbe_req.valid()) { + break; + } + _cmd_pool.mark_command_completed(cbe_req.tag(), + cbe_req.success()); + + module.drop_completed_client_request(cbe_req); + progress = true; + } + } + + void _execute_cbe_dump (bool &progress) + { + _cbe_dump.execute(_blk_buf); + if (_cbe_dump.execute_progress()) { + progress = true; + } + _handle_pending_blk_io_requests_of_module( + _cbe_dump, Module_type::CBE_DUMP, progress); + + _handle_completed_client_requests_of_module(_cbe_dump, progress); + } + + template + void _handle_pending_ta_requests_of_module(MODULE &module, + Module_type module_type, + bool &progress) + { + using Ta_operation = Cbe::Trust_anchor_request::Operation; + while (true) { + + if (!_trust_anchor.request_acceptable()) { + break; + } + Cbe::Trust_anchor_request ta_req = + module.peek_generated_ta_request(); + + if (ta_req.operation() == Ta_operation::INVALID) { + return; + } + Cbe::Trust_anchor_request typed_ta_req { ta_req }; + typed_ta_req.tag( + tag_set_module_type(typed_ta_req.tag(), module_type)); + + if (_verbose_node.ta_req_in_progress()) { + log("ta req in progress: ", typed_ta_req); + } + switch (ta_req.operation()) { + case Ta_operation::CREATE_KEY: + + _trust_anchor.submit_request(typed_ta_req); + module.drop_generated_ta_request(ta_req); + progress = true; + break; + + case Ta_operation::SECURE_SUPERBLOCK: + + _trust_anchor.submit_request_hash( + typed_ta_req, + module.peek_generated_ta_sb_hash(ta_req)); + + module.drop_generated_ta_request(ta_req); + progress = true; + break; + + case Ta_operation::ENCRYPT_KEY: + + _trust_anchor.submit_request_key_plaintext_value( + typed_ta_req, + module.peek_generated_ta_key_value_plaintext(ta_req)); + + module.drop_generated_ta_request(ta_req); + progress = true; + break; + + case Ta_operation::DECRYPT_KEY: + + _trust_anchor.submit_request_key_ciphertext_value( + typed_ta_req, + module.peek_generated_ta_key_value_ciphertext(ta_req)); + + module.drop_generated_ta_request(ta_req); + progress = true; + break; + + case Ta_operation::LAST_SB_HASH: + + _trust_anchor.submit_request(typed_ta_req); + module.drop_generated_ta_request(ta_req); + progress = true; + break; + + default: + + class Bad_operation { }; + throw Bad_operation { }; + } + } + } + + void _execute_cbe_init(bool &progress) + { + _cbe_init.execute(_blk_buf); + if (_cbe_init.execute_progress()) { + progress = true; + } + _handle_pending_blk_io_requests_of_module( + _cbe_init, Module_type::CBE_INIT, progress); + + _handle_pending_ta_requests_of_module( + _cbe_init, Module_type::CBE_INIT, progress); + + _handle_completed_client_requests_of_module(_cbe_init, progress); + } + + void _cbe_transfer_client_data_that_was_read(bool &progress) + { + while (true) { + + Cbe::Request request { }; + uint64_t vba { 0 }; + Crypto_plain_buffer::Index plain_buf_idx { 0 }; + _cbe->client_transfer_read_data_required( + request, vba, plain_buf_idx); + + if (!request.valid()) { + break; + } + _cmd_pool.verify_blk_data( + request, vba, _crypto_plain_buf.item(plain_buf_idx)); + + _cbe->client_transfer_read_data_in_progress(plain_buf_idx); + _cbe->client_transfer_read_data_completed(plain_buf_idx, true); + _benchmark.raise_nr_of_virt_blks_read(); + progress = true; + + if (_verbose_node.client_data_transferred()) { + log("client data: vba=", vba, " req=(", request, ")"); + } + } + } + + void _cbe_transfer_client_data_that_will_be_written(bool &progress) + { + while (true) { + + Cbe::Request request { }; + uint64_t vba { 0 }; + Crypto_plain_buffer::Index plain_buf_idx { 0 }; + _cbe->client_transfer_write_data_required( + request, vba, plain_buf_idx); + + if (!request.valid()) { + return; + } + _cmd_pool.generate_blk_data( + request, vba, _crypto_plain_buf.item(plain_buf_idx)); + + _cbe->client_transfer_write_data_in_progress(plain_buf_idx); + _cbe->client_transfer_write_data_completed( + plain_buf_idx, true); + + _benchmark.raise_nr_of_virt_blks_written(); + progress = true; + + if (_verbose_node.client_data_transferred()) { + log("client data: vba=", vba, " req=(", request, ")"); + } + } + } + + void _cbe_handle_crypto_add_key_requests(bool &progress) + { + while (true) { + + Key key; + Cbe::Request request { _cbe->crypto_add_key_required(key) }; + if (!request.valid()) { + return; + } + switch (_crypto.add_key(key)) { + case Crypto::Result::SUCCEEDED: + + if (_verbose_node.crypto_req_in_progress()) { + log("crypto req in progress: ", request); + } + _cbe->crypto_add_key_requested(request); + + if (_verbose_node.crypto_req_completed()) { + log("crypto req completed: ", request); + } + request.success(true); + _cbe->crypto_add_key_completed(request); + progress = true; + break; + + case Crypto::Result::FAILED: + + class Add_key_failed { }; + throw Add_key_failed { }; + + case Crypto::Result::RETRY_LATER: + + return; + } + } + } + + void _cbe_handle_crypto_remove_key_requests(bool &progress) + { + while (true) { + + Key::Id key_id; + Cbe::Request request { + _cbe->crypto_remove_key_required(key_id) }; + + if (!request.valid()) { + break; + } + switch (_crypto.remove_key(key_id)) { + case Crypto::Result::SUCCEEDED: + + if (_verbose_node.crypto_req_in_progress()) { + log("crypto req in progress: ", request); + } + _cbe->crypto_remove_key_requested(request); + + if (_verbose_node.crypto_req_completed()) { + log("crypto req completed: ", request); + } + request.success(true); + _cbe->crypto_remove_key_completed(request); + progress = true; + break; + + case Crypto::Result::FAILED: + + class Remove_key_failed { }; + throw Remove_key_failed { }; + + case Crypto::Result::RETRY_LATER: + + return; + } + } + } + + void _cbe_handle_crypto_encrypt_requests(bool &progress) + { + while (true) { + + if (!_crypto.request_acceptable()) { + break; + } + Crypto_plain_buffer::Index data_index { 0 }; + Cbe::Request request { + _cbe->crypto_cipher_data_required(data_index) }; + + if (!request.valid()) { + break; + } + request.tag(data_index.value); + _crypto.submit_request( + request, Crypto::Operation::ENCRYPT_BLOCK, + data_index, + Crypto_cipher_buffer::Index { data_index.value }); + + _cbe->crypto_cipher_data_requested(data_index); + if (_verbose_node.crypto_req_in_progress()) { + log("crypto req in progress: ", request); + } + progress = true; + } + } + + void _cbe_handle_crypto_decrypt_requests(bool &progress) + { + while (true) { + + if (!_crypto.request_acceptable()) { + break; + } + Crypto_cipher_buffer::Index data_index { 0 }; + Cbe::Request request { + _cbe->crypto_plain_data_required(data_index) }; + + if (!request.valid()) { + break; + } + request.tag(data_index.value); + _crypto.submit_request( + request, Crypto::Operation::DECRYPT_BLOCK, + Crypto_plain_buffer::Index { data_index.value }, + data_index); + + _cbe->crypto_plain_data_requested(data_index); + if (_verbose_node.crypto_req_in_progress()) { + log("crypto req in progress: ", request); + } + progress = true; + } + } + + void _cbe_handle_crypto_requests(bool &progress) + { + _cbe_handle_crypto_add_key_requests(progress); + _cbe_handle_crypto_remove_key_requests(progress); + _cbe_handle_crypto_encrypt_requests(progress); + _cbe_handle_crypto_decrypt_requests(progress); + } + + void _execute_cbe(bool &progress) + { + _cbe->execute(_blk_buf, _crypto_plain_buf, _crypto_cipher_buf); + if (_cbe->execute_progress()) { + progress = true; + } + _handle_pending_blk_io_requests_of_module( + *_cbe, Module_type::CBE, progress); + + _handle_pending_ta_requests_of_module( + *_cbe, Module_type::CBE, progress); + + _cbe_handle_crypto_requests(progress); + _cbe_transfer_client_data_that_was_read(progress); + _cbe_transfer_client_data_that_will_be_written(progress); + _handle_completed_client_requests_of_module(*_cbe, progress); + } + + void _cmd_pool_handle_pending_cbe_init_cmds(bool &progress) + { + while (true) { + + if (!_cbe_init.client_request_acceptable()) { + break; + } + Command const cmd { + _cmd_pool.peek_pending_command(Command::INITIALIZE) }; + + if (cmd.type() == Command::INVALID) { + break; + } + Cbe_init::Configuration const &cfg { cmd.initialize() }; + + _cbe_init.submit_client_request( + Cbe::Request( + Cbe::Request::Operation::READ, + false, 0, 0, 0, 0, cmd.id()), + cfg.vbd_nr_of_lvls() - 1, + cfg.vbd_nr_of_children(), + cfg.vbd_nr_of_leafs(), + cfg.ft_nr_of_lvls() - 1, + cfg.ft_nr_of_children(), + cfg.ft_nr_of_leafs()); + + _cmd_pool.mark_command_in_progress(cmd.id()); + progress = true; + } + } + + void _cmd_pool_handle_pending_check_cmds(bool &progress) + { + while (true) { + + if (!_cbe_check.client_request_acceptable()) { + break; + } + Command const cmd { + _cmd_pool.peek_pending_command(Command::CHECK) }; + + if (cmd.type() == Command::INVALID) { + break; + } + _cbe_check.submit_client_request( + Cbe::Request { + Cbe::Request::Operation::READ, + false, + 0, + 0, + 0, + 0, + cmd.id() + } + ); + _cmd_pool.mark_command_in_progress(cmd.id()); + progress = true; + } + } + + void _cmd_pool_handle_pending_cbe_cmds(bool &progress) + { + while (true) { + + if (!_cbe->client_request_acceptable()) { + break; + } + Command const cmd { + _cmd_pool.peek_pending_command(Command::REQUEST) }; + + if (cmd.type() == Command::INVALID) { + break; + } + Request_node const &req_node { cmd.request_node() }; + Cbe::Request const &cbe_req { + cmd.request_node().op(), + false, + req_node.has_attr_vba() ? req_node.vba() : 0, + 0, + req_node.has_attr_count() ? req_node.count() : 0, + 0, + cmd.id() }; + + _cbe->submit_client_request(cbe_req, 0); + _cmd_pool.mark_command_in_progress(cmd.id()); + progress = true; + } + } + + void _cmd_pool_handle_pending_ta_cmds(bool &progress) + { + while (true) { + + if (!_trust_anchor.request_acceptable()) { + break; + } + Command const cmd { + _cmd_pool.peek_pending_command(Command::TRUST_ANCHOR) }; + + if (cmd.type() == Command::INVALID) { + break; + } + Trust_anchor_node const &node { cmd.trust_anchor_node() }; + Trust_anchor_request const &ta_req { + node.op(), false, cmd.id() }; + + Trust_anchor_request typed_ta_req { ta_req }; + typed_ta_req.tag( + tag_set_module_type( + typed_ta_req.tag(), Module_type::CMD_POOL)); + + switch (node.op()) { + case Trust_anchor_request::Operation::INITIALIZE: + + _trust_anchor.submit_request_passphrase( + typed_ta_req, node.passphrase()); + + _cmd_pool.mark_command_in_progress(cmd.id()); + progress = true; + + break; + + default: + + class Bad_operation { }; + throw Bad_operation { }; + } + } + } + + void _cmd_pool_handle_pending_dump_cmds(bool &progress) + { + while (true) { + + if (!_cbe_dump.client_request_acceptable()) { + break; + } + Command const cmd { + _cmd_pool.peek_pending_command(Command::DUMP) }; + + if (cmd.type() == Command::INVALID) { + break; + } + Cbe_dump::Configuration const &cfg { cmd.dump() }; + _cbe_dump.submit_client_request( + Cbe::Request( + Cbe::Request::Operation::READ, + false, 0, 0, 0, 0, cmd.id()), + cfg); + + _cmd_pool.mark_command_in_progress(cmd.id()); + progress = true; + } + } + + void _cmd_pool_handle_pending_construct_cmds(bool &progress) + { + while (true) { + + Command const cmd { + _cmd_pool.peek_pending_command(Command::CONSTRUCT) }; + + if (cmd.type() == Command::INVALID) { + break; + } + _cbe.construct(); + _cmd_pool.mark_command_in_progress(cmd.id()); + _cmd_pool.mark_command_completed(cmd.id(), true); + progress = true; + } + } + + void _cmd_pool_handle_pending_destruct_cmds(bool &progress) + { + while (true) { + + Command const cmd { + _cmd_pool.peek_pending_command(Command::DESTRUCT) }; + + if (cmd.type() == Command::INVALID) { + break; + } + _cbe.destruct(); + _cmd_pool.mark_command_in_progress(cmd.id()); + _cmd_pool.mark_command_completed(cmd.id(), true); + progress = true; + } + } + + void _cmd_pool_handle_pending_list_snapshots_cmds(bool &progress) + { + while (true) { + + Command const cmd { + _cmd_pool.peek_pending_command(Command::LIST_SNAPSHOTS) }; + + if (cmd.type() == Command::INVALID) { + break; + } + Active_snapshot_ids ids; + _cbe->active_snapshot_ids(ids); + unsigned snap_nr { 0 }; + log(""); + log("List snapshots (command ID ", cmd.id(), ")"); + for (unsigned idx { 0 }; idx < sizeof(ids.values) / sizeof(ids.values[0]); idx++) { + if (ids.values[idx] != 0) { + log(" Snapshot #", snap_nr, " is generation ", ids.values[idx]); + snap_nr++; + } + } + log(""); + _cmd_pool.mark_command_in_progress(cmd.id()); + _cmd_pool.mark_command_completed(cmd.id(), true); + progress = true; + } + } + + void _cmd_pool_handle_pending_log_cmds(bool &progress) + { + while (true) { + + Command const cmd { + _cmd_pool.peek_pending_command(Command::LOG) }; + + if (cmd.type() == Command::INVALID) { + break; + } + log("\n", cmd.log_node().string(), "\n"); + _cmd_pool.mark_command_in_progress(cmd.id()); + _cmd_pool.mark_command_completed(cmd.id(), true); + progress = true; + } + } + + void _cmd_pool_handle_pending_benchmark_cmds(bool &progress) + { + while (true) { + + Command const cmd { + _cmd_pool.peek_pending_command(Command::BENCHMARK) }; + + if (cmd.type() == Command::INVALID) { + break; + } + _benchmark.submit_request(cmd.benchmark_node()); + _cmd_pool.mark_command_in_progress(cmd.id()); + _cmd_pool.mark_command_completed(cmd.id(), true); + progress = true; + } + } + + void _execute_cbe_check (bool &progress) + { + _cbe_check.execute(_blk_buf); + if (_cbe_check.execute_progress()) { + progress = true; + } + _handle_pending_blk_io_requests_of_module( + _cbe_check, Module_type::CBE_CHECK, progress); + + _handle_completed_client_requests_of_module(_cbe_check, progress); + } + + void _execute_command_pool(bool &progress) + { + if (_cbe.constructed()) { + _cmd_pool_handle_pending_cbe_cmds(progress); + _cmd_pool_handle_pending_list_snapshots_cmds(progress); + } + _cmd_pool_handle_pending_log_cmds(progress); + _cmd_pool_handle_pending_ta_cmds(progress); + _cmd_pool_handle_pending_cbe_init_cmds(progress); + _cmd_pool_handle_pending_benchmark_cmds(progress); + _cmd_pool_handle_pending_construct_cmds(progress); + _cmd_pool_handle_pending_destruct_cmds(progress); + _cmd_pool_handle_pending_dump_cmds(progress); + _cmd_pool_handle_pending_check_cmds(progress); + + if (_cmd_pool.nr_of_uncompleted_cmds() == 0) { + + if (_cmd_pool.nr_of_errors() > 0) { + + _cmd_pool.print_failed_cmds(); + _env.parent().exit(-1); + + } else { + + _env.parent().exit(0); + } + } + } + + template + void + _trust_anchor_handle_completed_requests_of_module(MODULE &module, + Trust_anchor_request const &typed_ta_req, + bool &progress) + { + using Ta_operation = Cbe::Trust_anchor_request::Operation; + + Trust_anchor_request ta_req { typed_ta_req }; + ta_req.tag(tag_unset_module_type(ta_req.tag())); + + if (_verbose_node.ta_req_completed()) { + log("ta req completed: ", typed_ta_req); + } + switch (ta_req.operation()) { + case Ta_operation::CREATE_KEY: + + module.mark_generated_ta_create_key_request_complete( + ta_req, + _trust_anchor.peek_completed_key_plaintext_value()); + + _trust_anchor.drop_completed_request(); + progress = true; + break; + + case Ta_operation::SECURE_SUPERBLOCK: + + module.mark_generated_ta_secure_sb_request_complete( + ta_req); + + _trust_anchor.drop_completed_request(); + progress = true; + break; + + case Ta_operation::LAST_SB_HASH: + + module.mark_generated_ta_last_sb_hash_request_complete( + ta_req, + _trust_anchor.peek_completed_hash()); + + _trust_anchor.drop_completed_request(); + progress = true; + break; + + case Ta_operation::ENCRYPT_KEY: + + module.mark_generated_ta_encrypt_key_request_complete( + ta_req, + _trust_anchor.peek_completed_key_ciphertext_value()); + + _trust_anchor.drop_completed_request(); + progress = true; + break; + + case Ta_operation::DECRYPT_KEY: + + module.mark_generated_ta_decrypt_key_request_complete( + ta_req, + _trust_anchor.peek_completed_key_plaintext_value()); + + _trust_anchor.drop_completed_request(); + progress = true; + break; + + default: + + class Bad_ta_operation { }; + throw Bad_ta_operation { }; + } + } + + void _trust_anchor_handle_completed_requests(bool &progress) + { + while (true) { + + Trust_anchor_request const typed_ta_req { + _trust_anchor.peek_completed_request() }; + + if (!typed_ta_req.valid()) { + break; + } + switch (tag_get_module_type(typed_ta_req.tag())) { + case Module_type::CMD_POOL: + { + Trust_anchor_request ta_req { typed_ta_req }; + ta_req.tag(tag_unset_module_type(ta_req.tag())); + + using Ta_operation = Trust_anchor_request::Operation; + if (ta_req.operation() == Ta_operation::INITIALIZE) { + + _cmd_pool.mark_command_completed(ta_req.tag(), + ta_req.success()); + + _trust_anchor.drop_completed_request(); + progress = true; + continue; + + } else { + + class Bad_operation { }; + throw Bad_operation { }; + } + break; + } + case Module_type::CBE_INIT: + + _trust_anchor_handle_completed_requests_of_module( + _cbe_init, typed_ta_req, progress); + + break; + + case Module_type::CBE: + + _trust_anchor_handle_completed_requests_of_module( + *_cbe, typed_ta_req, progress); + + break; + + default: + + class Bad_module_type { }; + throw Bad_module_type { }; + } + } + } + + void _execute_trust_anchor(bool &progress) + { + _trust_anchor.execute(progress); + _trust_anchor_handle_completed_requests(progress); + } + + void _crypto_handle_completed_encrypt_requests(bool &progress) + { + while (true) { + + Cbe::Request const request { + _crypto.peek_completed_encryption_request() }; + + if (!request.valid()) { + break; + } + Crypto_cipher_buffer::Index const data_idx { request.tag() }; + _cbe->supply_crypto_cipher_data(data_idx, request.success()); + + _crypto.drop_completed_request(); + progress = true; + + if (_verbose_node.crypto_req_completed()) { + log("crypto req completed: ", request); + } + } + } + + void _crypto_handle_completed_decrypt_requests(bool &progress) + { + while (true) { + + Cbe::Request const request { + _crypto.peek_completed_decryption_request() }; + + if (!request.valid()) { + break; + } + Crypto_plain_buffer::Index const data_idx { request.tag() }; + _cbe->supply_crypto_plain_data(data_idx, request.success()); + + _crypto.drop_completed_request(); + progress = true; + + if (_verbose_node.crypto_req_completed()) { + log("crypto req completed: ", request); + } + } + } + + void _execute_crypto(bool &progress) + { + _crypto.execute(_crypto_plain_buf, _crypto_cipher_buf, progress); + _crypto_handle_completed_encrypt_requests(progress); + _crypto_handle_completed_decrypt_requests(progress); + } + + void _execute() + { + bool progress { true }; + while (progress) { + + progress = false; + _execute_command_pool(progress); + _execute_cbe_init(progress); + + _blk_io.execute( + _cbe, _cbe_init, _cbe_dump, _cbe_check, _verbose_node, + _blk_buf, progress); + + _execute_trust_anchor(progress); + _execute_cbe_check(progress); + _execute_cbe_dump(progress); + _execute_crypto(progress); + if (_cbe.constructed()) { + _execute_cbe(progress); + } + } + } + + public: + + Main(Genode::Env &env) + : + _env { env } + { + _execute(); + } +}; + +void Component::construct(Genode::Env &env) +{ + env.exec_static_constructors(); + + Cbe::assert_valid_object_size(); + cbe_cxx_init(); + + Cbe::assert_valid_object_size(); + cbe_init_cxx_init(); + + Cbe::assert_valid_object_size(); + cbe_check_cxx_init(); + + Cbe::assert_valid_object_size(); + cbe_dump_cxx_init(); + + static Main main(env); +} + +extern "C" int memcmp(const void *p0, const void *p1, Genode::size_t size) +{ + return Genode::memcmp(p0, p1, size); +} diff --git a/repos/gems/src/app/cbe_tester/module_type.h b/repos/gems/src/app/cbe_tester/module_type.h new file mode 100644 index 0000000000..acf77f2743 --- /dev/null +++ b/repos/gems/src/app/cbe_tester/module_type.h @@ -0,0 +1,78 @@ +/* + * \brief Identifiers for the CBE modules used in the CBE tester + * \author Martin Stein + * \date 2020-08-26 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _MODULE_TYPE_H_ +#define _MODULE_TYPE_H_ + +/* Genode includes */ +#include + +enum class Module_type : Genode::uint8_t +{ + CBE_INIT, + CBE_DUMP, + CBE_CHECK, + CBE, +}; + + +static Module_type module_type_from_uint32(Genode::uint32_t uint32) +{ + class Bad_tag { }; + switch (uint32) { + case 1: return Module_type::CBE_INIT; + case 2: return Module_type::CBE; + case 3: return Module_type::CBE_DUMP; + case 4: return Module_type::CBE_CHECK; + default: throw Bad_tag(); + } +} + + +static Genode::uint32_t module_type_to_uint32(Module_type type) +{ + class Bad_type { }; + switch (type) { + case Module_type::CBE_INIT : return 1; + case Module_type::CBE : return 2; + case Module_type::CBE_DUMP : return 3; + case Module_type::CBE_CHECK: return 4; + } + throw Bad_type(); +} + + +static Module_type tag_get_module_type(Genode::uint32_t tag) +{ + return module_type_from_uint32((tag >> 24) & 0xff); +} + + +static Genode::uint32_t tag_set_module_type(Genode::uint32_t tag, + Module_type type) +{ + if (tag >> 24) { + + class Bad_tag { }; + throw Bad_tag(); + } + return tag | (module_type_to_uint32(type) << 24); +} + + +static Genode::uint32_t tag_unset_module_type(Genode::uint32_t tag) +{ + return tag & 0xffffff; +} + +#endif /* _MODULE_TYPE_H_ */ diff --git a/repos/gems/src/app/cbe_tester/target.mk b/repos/gems/src/app/cbe_tester/target.mk new file mode 100644 index 0000000000..6c1dffe287 --- /dev/null +++ b/repos/gems/src/app/cbe_tester/target.mk @@ -0,0 +1,10 @@ +REQUIRES := x86_64 + +TARGET := cbe_tester +SRC_CC += main.cc +SRC_CC += crypto.cc +SRC_CC += trust_anchor.cc +SRC_CC += vfs_utilities.cc + +INC_DIR := $(PRG_DIR) +LIBS += base cbe_cxx cbe_init_cxx cbe_check_cxx cbe_dump_cxx vfs diff --git a/repos/gems/src/app/cbe_tester/trust_anchor.cc b/repos/gems/src/app/cbe_tester/trust_anchor.cc new file mode 100644 index 0000000000..e8bce86a7c --- /dev/null +++ b/repos/gems/src/app/cbe_tester/trust_anchor.cc @@ -0,0 +1,606 @@ +/* + * \brief Implementation of the TA module API using the TA VFS API + * \author Martin Stein + * \date 2020-10-29 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* local includes */ +#include + +using namespace Genode; +using namespace Cbe; +using namespace Vfs; + + +void Trust_anchor::_execute_write_read_operation(Vfs_handle &file, + String<128> const &file_path, + char const *write_buf, + char *read_buf, + file_size read_size, + bool &progress) +{ + switch (_job.state) { + case Job_state::WRITE_PENDING: + + file.seek(_job.fl_offset); + _job.state = Job_state::WRITE_IN_PROGRESS; + progress = true; + return; + + case Job_state::WRITE_IN_PROGRESS: + { + file_size nr_of_written_bytes { 0 }; + Write_result result { Write_result::WRITE_ERR_INVALID }; + try { + result = + file.fs().write( + &file, write_buf + _job.fl_offset, + _job.fl_size, nr_of_written_bytes); + + } catch (Vfs::File_io_service::Insufficient_buffer) { + + return; + } + switch (result) { + case Write_result::WRITE_ERR_AGAIN: + case Write_result::WRITE_ERR_INTERRUPT: + case Write_result::WRITE_ERR_WOULD_BLOCK: + + return; + + case Write_result::WRITE_OK: + + _job.fl_offset += nr_of_written_bytes; + _job.fl_size -= nr_of_written_bytes; + + if (_job.fl_size > 0) { + + _job.state = Job_state::WRITE_PENDING; + progress = true; + return; + } + _job.state = Job_state::READ_PENDING; + _job.fl_offset = 0; + _job.fl_size = read_size; + progress = true; + return; + + default: + + _job.request.success(false); + error("failed to write file ", file_path); + _job.state = Job_state::COMPLETE; + progress = true; + return; + } + } + case Job_state::READ_PENDING: + + file.seek(_job.fl_offset); + + if (!file.fs().queue_read(&file, _job.fl_size)) { + return; + } + _job.state = Job_state::READ_IN_PROGRESS; + progress = true; + return; + + case Job_state::READ_IN_PROGRESS: + { + file_size nr_of_read_bytes { 0 }; + Read_result const result { + file.fs().complete_read( + &file, read_buf + _job.fl_offset, _job.fl_size, + nr_of_read_bytes) }; + + switch (result) { + case Read_result::READ_QUEUED: + case Read_result::READ_ERR_INTERRUPT: + case Read_result::READ_ERR_AGAIN: + case Read_result::READ_ERR_WOULD_BLOCK: + + return; + + case Read_result::READ_OK: + + _job.fl_offset += nr_of_read_bytes; + _job.fl_size -= nr_of_read_bytes; + _job.request.success(true); + + if (_job.fl_size > 0) { + + _job.state = Job_state::READ_PENDING; + progress = true; + return; + } + _job.state = Job_state::COMPLETE; + progress = true; + return; + + default: + + _job.request.success(false); + error("failed to read file ", file_path); + _job.state = Job_state::COMPLETE; + return; + } + } + default: + + return; + } +} + + +void Trust_anchor::_execute_write_operation(Vfs_handle &file, + String<128> const &file_path, + char const *write_buf, + bool &progress) +{ + switch (_job.state) { + case Job_state::WRITE_PENDING: + + file.seek(_job.fl_offset); + _job.state = Job_state::WRITE_IN_PROGRESS; + progress = true; + return; + + case Job_state::WRITE_IN_PROGRESS: + { + file_size nr_of_written_bytes { 0 }; + Write_result result { Write_result::WRITE_ERR_INVALID }; + try { + result = + file.fs().write( + &file, write_buf + _job.fl_offset, + _job.fl_size, nr_of_written_bytes); + + } catch (Vfs::File_io_service::Insufficient_buffer) { + + return; + } + switch (result) { + case Write_result::WRITE_ERR_AGAIN: + case Write_result::WRITE_ERR_INTERRUPT: + case Write_result::WRITE_ERR_WOULD_BLOCK: + + return; + + case Write_result::WRITE_OK: + + _job.fl_offset += nr_of_written_bytes; + _job.fl_size -= nr_of_written_bytes; + + if (_job.fl_size > 0) { + + _job.state = Job_state::WRITE_PENDING; + progress = true; + return; + } + _job.state = Job_state::READ_PENDING; + _job.fl_offset = 0; + _job.fl_size = 0; + progress = true; + return; + + default: + + _job.request.success(false); + error("failed to write file ", file_path); + _job.state = Job_state::COMPLETE; + progress = true; + return; + } + } + case Job_state::READ_PENDING: + + file.seek(_job.fl_offset); + + if (!file.fs().queue_read(&file, _job.fl_size)) { + return; + } + _job.state = Job_state::READ_IN_PROGRESS; + progress = true; + return; + + case Job_state::READ_IN_PROGRESS: + { + file_size nr_of_read_bytes { 0 }; + Read_result const result { + file.fs().complete_read( + &file, _read_buf + _job.fl_offset, _job.fl_size, + nr_of_read_bytes) }; + + switch (result) { + case Read_result::READ_QUEUED: + case Read_result::READ_ERR_INTERRUPT: + case Read_result::READ_ERR_AGAIN: + case Read_result::READ_ERR_WOULD_BLOCK: + + return; + + case Read_result::READ_OK: + + _job.fl_offset += nr_of_read_bytes; + _job.fl_size -= nr_of_read_bytes; + _job.request.success(true); + + if (_job.fl_size > 0) { + + _job.state = Job_state::READ_PENDING; + progress = true; + return; + } + _job.state = Job_state::COMPLETE; + progress = true; + return; + + default: + + _job.request.success(false); + error("failed to read file ", file_path); + _job.state = Job_state::COMPLETE; + return; + } + } + default: + + return; + } +} + + +void Trust_anchor::_execute_read_operation(Vfs_handle &file, + String<128> const &file_path, + char *read_buf, + bool &progress) +{ + switch (_job.state) { + case Job_state::READ_PENDING: + + file.seek(_job.fl_offset); + + if (!file.fs().queue_read(&file, _job.fl_size)) { + return; + } + _job.state = Job_state::READ_IN_PROGRESS; + progress = true; + return; + + case Job_state::READ_IN_PROGRESS: + { + file_size nr_of_read_bytes { 0 }; + Read_result const result { + file.fs().complete_read( + &file, read_buf + _job.fl_offset, _job.fl_size, + nr_of_read_bytes) }; + + switch (result) { + case Read_result::READ_QUEUED: + case Read_result::READ_ERR_INTERRUPT: + case Read_result::READ_ERR_AGAIN: + case Read_result::READ_ERR_WOULD_BLOCK: + + return; + + case Read_result::READ_OK: + + _job.fl_offset += nr_of_read_bytes; + _job.fl_size -= nr_of_read_bytes; + _job.request.success(true); + + if (_job.fl_size > 0) { + + _job.state = Job_state::READ_PENDING; + progress = true; + return; + } + _job.state = Job_state::COMPLETE; + progress = true; + return; + + default: + + _job.request.success(false); + error("failed to read file ", file_path); + _job.state = Job_state::COMPLETE; + return; + } + } + default: + + return; + } +} + + +Trust_anchor::Trust_anchor(Vfs::Env &vfs_env, + Xml_node const &xml_node, + Signal_context_capability sigh) +: + _vfs_env { vfs_env }, + _handler { sigh }, + _path { xml_node.attribute_value("path", String<128>()) } +{ + _initialize_file.handler(&_handler); + _hashsum_file.handler(&_handler); + _generate_key_file.handler(&_handler); + _encrypt_file.handler(&_handler); + _decrypt_file.handler(&_handler); +} + + +bool Trust_anchor::request_acceptable() const +{ + return _job.request.operation() == Operation::INVALID; +} + + +void +Trust_anchor::submit_request_passphrase(Trust_anchor_request const &request, + String<64> const &passphrase) +{ + switch (request.operation()) { + case Operation::INITIALIZE: + + _job.request = request; + _job.passphrase = passphrase; + _job.state = Job_state::WRITE_PENDING; + _job.fl_offset = 0; + _job.fl_size = _job.passphrase.length(); + break; + + default: + + class Bad_operation { }; + throw Bad_operation { }; + } +} + + +void +Trust_anchor:: +submit_request_key_plaintext_value(Trust_anchor_request const &request, + Key_plaintext_value const &key_plaintext_value) +{ + switch (request.operation()) { + case Operation::ENCRYPT_KEY: + + _job.request = request; + _job.key_plaintext_value = key_plaintext_value; + _job.state = Job_state::WRITE_PENDING; + _job.fl_offset = 0; + _job.fl_size = sizeof(_job.key_plaintext_value.value) / + sizeof(_job.key_plaintext_value.value[0]); + break; + + default: + + class Bad_operation { }; + throw Bad_operation { }; + } +} + + +void +Trust_anchor:: +submit_request_key_ciphertext_value(Trust_anchor_request const &request, + Key_ciphertext_value const &key_ciphertext_value) +{ + switch (request.operation()) { + case Operation::DECRYPT_KEY: + + _job.request = request; + _job.key_ciphertext_value = key_ciphertext_value; + _job.state = Job_state::WRITE_PENDING; + _job.fl_offset = 0; + _job.fl_size = sizeof(_job.key_ciphertext_value.value) / + sizeof(_job.key_ciphertext_value.value[0]); + break; + + default: + + class Bad_operation { }; + throw Bad_operation { }; + } +} + + +void Trust_anchor::submit_request_hash(Trust_anchor_request const &request, + Hash const &hash) +{ + switch (request.operation()) { + case Operation::SECURE_SUPERBLOCK: + + _job.request = request; + _job.hash = hash; + _job.state = Job_state::WRITE_PENDING; + _job.fl_offset = 0; + _job.fl_size = sizeof(_job.hash.values) / + sizeof(_job.hash.values[0]); + break; + + default: + + class Bad_operation { }; + throw Bad_operation { }; + } +} + +void Trust_anchor::submit_request(Trust_anchor_request const &request) +{ + switch (request.operation()) { + case Operation::LAST_SB_HASH: + + _job.request = request; + _job.state = Job_state::READ_PENDING; + _job.fl_offset = 0; + _job.fl_size = sizeof(_job.hash.values) / + sizeof(_job.hash.values[0]); + break; + + case Operation::CREATE_KEY: + + _job.request = request; + _job.state = Job_state::READ_PENDING; + _job.fl_offset = 0; + _job.fl_size = sizeof(_job.key_plaintext_value.value) / + sizeof(_job.key_plaintext_value.value[0]); + break; + + default: + + class Bad_operation { }; + throw Bad_operation { }; + } +} + + +void Trust_anchor::execute(bool &progress) +{ + switch (_job.request.operation()) { + case Operation::INITIALIZE: + + _execute_write_operation( + _initialize_file, _initialize_path, + _job.passphrase.string(), progress); + + break; + + case Operation::SECURE_SUPERBLOCK: + + _execute_write_operation( + _hashsum_file, _hashsum_path, + _job.hash.values, progress); + + break; + + case Operation::LAST_SB_HASH: + + _execute_read_operation( + _hashsum_file, _hashsum_path, + _job.hash.values, progress); + + break; + + case Operation::CREATE_KEY: + + _execute_read_operation( + _generate_key_file, _generate_key_path, + _job.key_plaintext_value.value, progress); + + break; + + case Operation::ENCRYPT_KEY: + + _execute_write_read_operation( + _encrypt_file, _encrypt_path, + _job.key_plaintext_value.value, + _job.key_ciphertext_value.value, + sizeof(_job.key_ciphertext_value.value) / + sizeof(_job.key_ciphertext_value.value[0]), + progress); + + break; + + case Operation::DECRYPT_KEY: + + _execute_write_read_operation( + _decrypt_file, _decrypt_path, + _job.key_ciphertext_value.value, + _job.key_plaintext_value.value, + sizeof(_job.key_plaintext_value.value) / + sizeof(_job.key_plaintext_value.value[0]), + progress); + + break; + + case Operation::INVALID: + + break; + + default: + + class Bad_operation { }; + throw Bad_operation { }; + } +} + + +Trust_anchor_request Trust_anchor::peek_completed_request() const +{ + if (_job.state != Job_state::COMPLETE) { + + return Trust_anchor_request { }; + } + return _job.request; +} + + +Hash const &Trust_anchor::peek_completed_hash() const +{ + if (_job.request.operation() != Operation::LAST_SB_HASH) { + + class Bad_operation { }; + throw Bad_operation { }; + } + if (_job.state != Job_state::COMPLETE) { + + class Bad_state { }; + throw Bad_state { }; + } + return _job.hash; +} + + +Key_plaintext_value const & +Trust_anchor::peek_completed_key_plaintext_value() const +{ + if (_job.request.operation() != Operation::CREATE_KEY && + _job.request.operation() != Operation::DECRYPT_KEY) { + + class Bad_operation { }; + throw Bad_operation { }; + } + if (_job.state != Job_state::COMPLETE) { + + class Bad_state { }; + throw Bad_state { }; + } + return _job.key_plaintext_value; +} + + +Key_ciphertext_value const & +Trust_anchor::peek_completed_key_ciphertext_value() const +{ + if (_job.request.operation() != Operation::ENCRYPT_KEY) { + + class Bad_operation { }; + throw Bad_operation { }; + } + if (_job.state != Job_state::COMPLETE) { + + class Bad_state { }; + throw Bad_state { }; + } + return _job.key_ciphertext_value; +} + + +void Trust_anchor::drop_completed_request() +{ + if (_job.state != Job_state::COMPLETE) { + + class Bad_state { }; + throw Bad_state { }; + } + _job.request = Trust_anchor_request { }; +} diff --git a/repos/gems/src/app/cbe_tester/trust_anchor.h b/repos/gems/src/app/cbe_tester/trust_anchor.h new file mode 100644 index 0000000000..50bebc4939 --- /dev/null +++ b/repos/gems/src/app/cbe_tester/trust_anchor.h @@ -0,0 +1,124 @@ +/* + * \brief Implementation of the TA module API using the TA VFS API + * \author Martin Stein + * \date 2020-10-29 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _CBE_TESTER__TRUST_ANCHOR_H_ +#define _CBE_TESTER__TRUST_ANCHOR_H_ + +/* CBE includes */ +#include + +/* CBE tester includes */ +#include + +class Trust_anchor +{ + private: + + using Read_result = Vfs::File_io_service::Read_result; + using Write_result = Vfs::File_io_service::Write_result; + using Operation = Cbe::Trust_anchor_request::Operation; + + enum Job_state + { + WRITE_PENDING, + WRITE_IN_PROGRESS, + READ_PENDING, + READ_IN_PROGRESS, + COMPLETE + }; + + struct Job + { + Cbe::Trust_anchor_request request { }; + Job_state state { Job_state::COMPLETE }; + Genode::String<64> passphrase { }; + Cbe::Hash hash { }; + Cbe::Key_plaintext_value key_plaintext_value { }; + Cbe::Key_ciphertext_value key_ciphertext_value { }; + Vfs::file_offset fl_offset { 0 }; + Vfs::file_size fl_size { 0 }; + }; + + Vfs::Env &_vfs_env; + char _read_buf[64]; + Vfs_io_response_handler _handler; + Genode::String<128> const _path; + Genode::String<128> const _decrypt_path { _path, "/decrypt" }; + Vfs::Vfs_handle &_decrypt_file { vfs_open_rw(_vfs_env, { _decrypt_path }) }; + Genode::String<128> const _encrypt_path { _path, "/encrypt" }; + Vfs::Vfs_handle &_encrypt_file { vfs_open_rw(_vfs_env, { _encrypt_path }) }; + Genode::String<128> const _generate_key_path { _path, "/generate_key" }; + Vfs::Vfs_handle &_generate_key_file { vfs_open_rw(_vfs_env, { _generate_key_path }) }; + Genode::String<128> const _initialize_path { _path, "/initialize" }; + Vfs::Vfs_handle &_initialize_file { vfs_open_rw(_vfs_env, { _initialize_path }) }; + Genode::String<128> const _hashsum_path { _path, "/hashsum" }; + Vfs::Vfs_handle &_hashsum_file { vfs_open_rw(_vfs_env, { _hashsum_path }) }; + Job _job { }; + + void _execute_write_read_operation(Vfs::Vfs_handle &file, + Genode::String<128> const &file_path, + char const *write_buf, + char *read_buf, + Vfs::file_size read_size, + bool &progress); + + void _execute_write_operation(Vfs::Vfs_handle &file, + Genode::String<128> const &file_path, + char const *write_buf, + bool &progress); + + void _execute_read_operation(Vfs::Vfs_handle &file, + Genode::String<128> const &file_path, + char *read_buf, + bool &progress); + + public: + + Trust_anchor(Vfs::Env &vfs_env, + Genode::Xml_node const &xml_node, + Genode::Signal_context_capability sigh); + + bool request_acceptable() const; + + void submit_request_passphrase(Cbe::Trust_anchor_request const &request, + Genode::String<64> const &passphrase); + + void + submit_request_key_plaintext_value(Cbe::Trust_anchor_request const &request, + Cbe::Key_plaintext_value const &key_plaintext_value); + + void + submit_request_key_ciphertext_value(Cbe::Trust_anchor_request const &request, + Cbe::Key_ciphertext_value const &key_ciphertext_value); + + void submit_request_hash(Cbe::Trust_anchor_request const &request, + Cbe::Hash const &hash); + + void submit_request(Cbe::Trust_anchor_request const &request); + + void execute(bool &progress); + + Cbe::Trust_anchor_request peek_completed_request() const; + + Cbe::Hash const &peek_completed_hash() const; + + Cbe::Key_plaintext_value const & + peek_completed_key_plaintext_value() const; + + Cbe::Key_ciphertext_value const & + peek_completed_key_ciphertext_value() const; + + void drop_completed_request(); +}; + +#endif /* _CBE_TESTER__TRUST_ANCHOR_H_ */ diff --git a/repos/gems/src/app/cbe_tester/util.h b/repos/gems/src/app/cbe_tester/util.h new file mode 100644 index 0000000000..7bec415742 --- /dev/null +++ b/repos/gems/src/app/cbe_tester/util.h @@ -0,0 +1,155 @@ +/* + * \brief Integration of the Consistent Block Encrypter (CBE) + * \author Martin Stein + * \author Josef Soentgen + * \date 2020-11-10 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _UTIL_H_ +#define _UTIL_H_ + +/* Genode includes */ +#include +#include +#include +#include + + +namespace Util { + + using namespace Genode; + + using sector_t = Block::sector_t; + + struct Block_io; + + /* + * 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.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_ */ diff --git a/repos/gems/src/app/cbe_tester/verbose_node.h b/repos/gems/src/app/cbe_tester/verbose_node.h new file mode 100644 index 0000000000..3b94a30a98 --- /dev/null +++ b/repos/gems/src/app/cbe_tester/verbose_node.h @@ -0,0 +1,69 @@ +/* + * \brief Verbosity configuration of the CBE tester + * \author Martin Stein + * \date 2020-10-29 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _CBE_TESTER__VERBOSE_NODE_H_ +#define _CBE_TESTER__VERBOSE_NODE_H_ + +/* Genode includes */ +#include + +class Verbose_node +{ + private: + + bool _cmd_pool_cmd_pending { false }; + bool _cmd_pool_cmd_in_progress { false }; + bool _cmd_pool_cmd_completed { false }; + bool _blk_io_req_in_progress { false }; + bool _blk_io_req_completed { false }; + bool _ta_req_in_progress { false }; + bool _ta_req_completed { false }; + bool _crypto_req_completed { false }; + bool _crypto_req_in_progress { false }; + bool _client_data_mismatch { false }; + bool _client_data_transferred { false }; + + public: + + Verbose_node(Genode::Xml_node const &config) + { + config.with_sub_node("verbose", [&] (Genode::Xml_node const &verbose) + { + _cmd_pool_cmd_pending = verbose.attribute_value("cmd_pool_cmd_pending" , false); + _cmd_pool_cmd_in_progress = verbose.attribute_value("cmd_pool_cmd_in_progress", false); + _cmd_pool_cmd_completed = verbose.attribute_value("cmd_pool_cmd_completed" , false); + _blk_io_req_in_progress = verbose.attribute_value("blk_io_req_in_progress" , false); + _blk_io_req_completed = verbose.attribute_value("blk_io_req_completed" , false); + _ta_req_in_progress = verbose.attribute_value("ta_req_in_progress" , false); + _ta_req_completed = verbose.attribute_value("ta_req_completed" , false); + _crypto_req_completed = verbose.attribute_value("crypto_req_completed" , false); + _crypto_req_in_progress = verbose.attribute_value("crypto_req_in_progress" , false); + _client_data_mismatch = verbose.attribute_value("client_data_mismatch" , false); + _client_data_transferred = verbose.attribute_value("client_data_transferred" , false); + }); + } + + bool cmd_pool_cmd_pending () const { return _cmd_pool_cmd_pending ; } + bool cmd_pool_cmd_in_progress() const { return _cmd_pool_cmd_in_progress; } + bool cmd_pool_cmd_completed () const { return _cmd_pool_cmd_completed ; } + bool blk_io_req_in_progress () const { return _blk_io_req_in_progress ; } + bool blk_io_req_completed () const { return _blk_io_req_completed ; } + bool ta_req_in_progress () const { return _ta_req_in_progress ; } + bool ta_req_completed () const { return _ta_req_completed ; } + bool crypto_req_completed () const { return _crypto_req_completed ; } + bool crypto_req_in_progress () const { return _crypto_req_in_progress ; } + bool client_data_mismatch () const { return _client_data_mismatch ; } + bool client_data_transferred () const { return _client_data_transferred ; } +}; + +#endif /* _CBE_TESTER__VERBOSE_NODE_H_ */ diff --git a/repos/gems/src/app/cbe_tester/vfs_utilities.cc b/repos/gems/src/app/cbe_tester/vfs_utilities.cc new file mode 100644 index 0000000000..bddc2e48e5 --- /dev/null +++ b/repos/gems/src/app/cbe_tester/vfs_utilities.cc @@ -0,0 +1,74 @@ +/* + * \brief Utilities for a more convenient use of the VFS + * \author Martin Stein + * \date 2020-10-29 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* CBE tester includes */ +#include + +using namespace Genode; +using namespace Vfs; + + +/***************************** + ** Vfs_io_response_handler ** + *****************************/ + +Vfs_io_response_handler::Vfs_io_response_handler(Genode::Signal_context_capability sigh) +: + _sigh(sigh) +{ } + + +void Vfs_io_response_handler::read_ready_response() { } + + +void Vfs_io_response_handler::io_progress_response() +{ + Signal_transmitter(_sigh).submit(); +} + + +/********************** + ** Global functions ** + **********************/ + +Vfs::Vfs_handle &vfs_open(Vfs::Env &vfs_env, + Genode::String<128> path, + Vfs::Directory_service::Open_mode mode) +{ + Vfs_handle *handle { nullptr }; + Directory_service::Open_result const result { + vfs_env.root_dir().open( + path.string(), mode, &handle, vfs_env.alloc()) }; + + if (result != Directory_service::Open_result::OPEN_OK) { + + error("failed to open file ", path.string()); + class Failed { }; + throw Failed { }; + } + return *handle; +} + + +Vfs_handle &vfs_open_wo(Vfs::Env &vfs_env, + String<128> path) +{ + return vfs_open(vfs_env, path, Directory_service::OPEN_MODE_WRONLY); +} + + +Vfs::Vfs_handle &vfs_open_rw(Vfs::Env &vfs_env, + Genode::String<128> path) +{ + return vfs_open(vfs_env, path, Directory_service::OPEN_MODE_RDWR); +} diff --git a/repos/gems/src/app/cbe_tester/vfs_utilities.h b/repos/gems/src/app/cbe_tester/vfs_utilities.h new file mode 100644 index 0000000000..4f98a48445 --- /dev/null +++ b/repos/gems/src/app/cbe_tester/vfs_utilities.h @@ -0,0 +1,54 @@ +/* + * \brief Utilities for a more convenient use of the VFS + * \author Martin Stein + * \date 2020-10-29 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _CBE_TESTER__VFS_UTILITIES_H_ +#define _CBE_TESTER__VFS_UTILITIES_H_ + +/* Genode includes */ +#include +#include + +class Vfs_io_response_handler : public Vfs::Io_response_handler +{ + private: + + Genode::Signal_context_capability const _sigh; + + public: + + Vfs_io_response_handler(Genode::Signal_context_capability sigh); + + + /****************************** + ** Vfs::Io_response_handler ** + ******************************/ + + void read_ready_response() override; + + void io_progress_response() override; +}; + + +Vfs::Vfs_handle &vfs_open(Vfs::Env &vfs_env, + Genode::String<128> path, + Vfs::Directory_service::Open_mode mode); + + +Vfs::Vfs_handle &vfs_open_wo(Vfs::Env &vfs_env, + Genode::String<128> path); + + +Vfs::Vfs_handle &vfs_open_rw(Vfs::Env &vfs_env, + Genode::String<128> path); + +#endif /* _CBE_TESTER__VFS_UTILITIES_H_ */ diff --git a/repos/gems/src/app/vfs_replay/component.cc b/repos/gems/src/app/vfs_replay/component.cc new file mode 100644 index 0000000000..91f5889cd9 --- /dev/null +++ b/repos/gems/src/app/vfs_replay/component.cc @@ -0,0 +1,543 @@ +/* + * \brief VFS replay tool + * \author Josef Soentgen + * \date 2020-03-18 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include + + +using namespace Genode; +using Vfs::file_offset; +using Vfs::file_size; + + +class Vfs_replay +{ + private: + + Vfs_replay(const Vfs_replay&) = delete; + Vfs_replay& operator=(const Vfs_replay&) = delete; + + + Env &_env; + + Vfs::File_system &_vfs; + Vfs::Vfs_handle *_vfs_handle; + + Attached_ram_dataspace _write_buffer; + Attached_ram_dataspace _read_buffer; + + bool _verbose; + + Xml_node _replay_node; + Xml_node _request_node { "" }; + unsigned _num_requests { 0 }; + unsigned _curr_request_id { 0 }; + + bool _finished { false }; + + struct Request + { + enum Type { INVALID, READ, WRITE, SYNC, }; + + static char const *type_to_string(Type t) + { + switch (t) { + case Type::INVALID: return "INVALID"; + case Type::READ: return "READ"; + case Type::WRITE: return "WRITE"; + case Type::SYNC: return "SYNC"; + } + return ""; + } + + enum State { + NONE, + READ_PENDING, READ_IN_PROGRESS, READ_COMPLETE, + WRITE_PENDING, WRITE_IN_PROGRESS, WRITE_COMPLETE, + SYNC_PENDING, SYNC_IN_PROGRESS, SYNC_COMPLETE, + ERROR, + }; + + static char const *state_to_string(State s) + { + switch (s) { + case State::NONE: return "NONE"; + case State::READ_PENDING: return "READ_PENDING"; + case State::READ_IN_PROGRESS: return "READ_IN_PROGRESS"; + case State::READ_COMPLETE: return "READ_COMPLETE"; + case State::WRITE_PENDING: return "WRITE_PENDING"; + case State::WRITE_IN_PROGRESS: return "WRITE_IN_PROGRESS"; + case State::WRITE_COMPLETE: return "WRITE_COMPLETE"; + case State::SYNC_PENDING: return "SYNC_PENDING"; + case State::SYNC_IN_PROGRESS: return "SYNC_IN_PROGRESS"; + case State::SYNC_COMPLETE: return "SYNC_COMPLETE"; + case State::ERROR: return "ERROR"; + } + return ""; + } + + Type type; + State state; + file_offset offset; + file_size count; + file_size out_count; + + file_offset current_offset; + file_size current_count; + + bool success; + bool complete; + + bool pending() const { return state != NONE; } + bool idle() const { return state == NONE; } + + void print(Genode::Output &out) const + { + Genode::print(out, "[ type: ", type_to_string(type), + " state: ", state_to_string(state), + " offset: ", offset, + " count: ", count, + " out_count: ", out_count, + " current_offset: ", current_offset, + " current_count: ", current_count, + " success: ", success, + " complete: ", complete, " ]"); + } + }; + + static Request::Type string_to_type(char const *string) + { + enum { READ_LEN = 4, WRITE_LEN = 5, SYNC_LEN = 4, }; + + if (Genode::strcmp(string, "read", READ_LEN) == 0) { + return Request::Type::READ; + } else + + if (Genode::strcmp(string, "write", WRITE_LEN) == 0) { + return Request::Type::WRITE; + } else + + if (Genode::strcmp(string, "sync", SYNC_LEN) == 0) { + return Request::Type::SYNC; + } else + + return Request::Type::INVALID; + } + + Request _current_request { }; + + bool _read(Request &request) + { + bool progress = false; + + switch (request.state) { + case Request::State::NONE: + + if (request.count > _read_buffer.size()) { + struct Buffer_too_small { }; + throw Buffer_too_small (); + } + + request.state = Request::State::READ_PENDING; + progress = true; + [[fallthrough]]; + case Request::State::READ_PENDING: + + _vfs_handle->seek(request.current_offset); + if (!_vfs_handle->fs().queue_read(_vfs_handle, request.current_count)) { + return progress; + } + + request.state = Request::State::READ_IN_PROGRESS; + progress = true; + [[fallthrough]]; + case Request::State::READ_IN_PROGRESS: + { + using Result = Vfs::File_io_service::Read_result; + + bool completed = false; + file_size out = 0; + + Result const result = + _vfs_handle->fs().complete_read(_vfs_handle, + _read_buffer.local_addr(), + request.current_count, out); + if ( result == Result::READ_QUEUED + || result == Result::READ_ERR_INTERRUPT + || result == Result::READ_ERR_AGAIN + || result == Result::READ_ERR_WOULD_BLOCK) { + return progress; + } + + if (result == Result::READ_OK) { + request.current_offset += out; + request.current_count -= out; + request.success = true; + } else + + if ( result == Result::READ_ERR_IO + || result == Result::READ_ERR_INVALID) { + request.success = false; + completed = true; + } + + if (request.current_count == 0 || completed) { + request.state = Request::State::READ_COMPLETE; + } else { + request.state = Request::State::READ_PENDING; + return progress; + } + progress = true; + } + [[fallthrough]]; + case Request::State::READ_COMPLETE: + + request.state = Request::State::NONE; + request.complete = true; + progress = true; + default: break; + } + + return progress; + } + + bool _write(Request &request) + { + bool progress = false; + + switch (request.state) { + case Request::State::NONE: + + if (request.count > _write_buffer.size()) { + struct Buffer_too_small { }; + throw Buffer_too_small (); + } + + request.state = Request::State::WRITE_PENDING; + progress = true; + [[fallthrough]]; + case Request::State::WRITE_PENDING: + + _vfs_handle->seek(request.current_offset); + + request.state = Request::State::WRITE_IN_PROGRESS; + progress = true; + [[fallthrough]]; + case Request::State::WRITE_IN_PROGRESS: + { + using Result = Vfs::File_io_service::Write_result; + + bool completed = false; + file_size out = 0; + + Result result = Result::WRITE_ERR_INVALID; + try { + result = _vfs_handle->fs().write(_vfs_handle, + _write_buffer.local_addr(), + request.current_count, out); + } catch (Vfs::File_io_service::Insufficient_buffer) { + return progress; + } + if ( result == Result::WRITE_ERR_AGAIN + || result == Result::WRITE_ERR_INTERRUPT + || result == Result::WRITE_ERR_WOULD_BLOCK) { + return progress; + } + if (result == Result::WRITE_OK) { + request.current_offset += out; + request.current_count -= out; + request.success = true; + } + + if ( result == Result::WRITE_ERR_IO + || result == Result::WRITE_ERR_INVALID) { + request.success = false; + completed = true; + } + if (request.current_count == 0 || completed) { + request.state = Request::State::WRITE_COMPLETE; + } else { + request.state = Request::State::WRITE_PENDING; + return progress; + } + progress = true; + } + [[fallthrough]]; + case Request::State::WRITE_COMPLETE: + + request.state = Request::State::NONE; + request.complete = true; + progress = true; + default: break; + } + + return progress; + } + + bool _sync(Request &request) + { + bool progress = false; + + switch (request.state) { + case Request::State::NONE: + + request.state = Request::State::SYNC_PENDING; + progress = true; + [[fallthrough]]; + case Request::State::SYNC_PENDING: + + if (!_vfs_handle->fs().queue_sync(_vfs_handle)) { + return progress; + } + request.state = Request::State::SYNC_IN_PROGRESS; + progress = true; + [[fallthrough]]; + case Request::State::SYNC_IN_PROGRESS: + { + using Result = Vfs::File_io_service::Sync_result; + Result const result = _vfs_handle->fs().complete_sync(_vfs_handle); + + if (result == Result::SYNC_QUEUED) { + return progress; + } + + if (result == Result::SYNC_ERR_INVALID) { + request.success = false; + } + + if (result == Result::SYNC_OK) { + request.success = true; + } + + request.state = Request::State::SYNC_COMPLETE; + progress = true; + } + [[fallthrough]]; + case Request::State::SYNC_COMPLETE: + + request.state = Request::State::NONE; + request.complete = true; + progress = true; + default: break; + } + + return progress; + } + + bool _handle_request(Genode::Xml_node const &node) + { + if (!_current_request.pending()) { + + file_offset const offset = node.attribute_value("offset", file_size(~0llu)); + file_size const count = node.attribute_value("count", file_size(~0llu)); + + using Type_String = String<16>; + Type_String const type_string = node.attribute_value("type", Type_String()); + Request::Type const type = string_to_type(type_string.string()); + + _current_request.type = type; + + if (type != Request::Type::INVALID) { + _current_request = { + .type = type, + .state = Request::State::NONE, + .offset = offset, + .count = count, + .out_count = 0, + .current_offset = offset, + .current_count = count, + .success = false, + .complete = false, + }; + if (_verbose) { + log("Next request: id: ", _curr_request_id, " ", + _current_request); + } + } + } + + switch (_current_request.type) { + case Request::Type::READ: + return _read(_current_request); + case Request::Type::WRITE: + return _write(_current_request); + case Request::Type::SYNC: + return _sync(_current_request); + case Request::Type::INVALID: + _current_request.complete = true; + return true; + } + return false; + } + + void _process_replay() + { + bool failed = false; + + while (true) { + + bool const progress = _handle_request(_request_node); + if (!progress) { break; } + + if (_current_request.complete) { + if (_verbose) { + log("Completed request: ", _current_request); + } + + if (!_current_request.success) { + error("current request: ", _current_request, " failed"); + failed = true; + _finished = true; + break; + } + + try { + _request_node = _replay_node.sub_node(++_curr_request_id); + } catch (Xml_node::Nonexistent_sub_node) { + _finished = true; + break; + } + } + } + + if (_finished) { + _env.parent().exit(failed ? 1 : 0); + } + } + + struct Io_response_handler : Vfs::Io_response_handler + { + Genode::Signal_context_capability sigh { }; + + void read_ready_response() override { } + + void io_progress_response() override + { + if (sigh.valid()) { + Genode::Signal_transmitter(sigh).submit(); + } + } + }; + + Io_response_handler _io_response_handler { }; + + public: + + Vfs_replay(Vfs::File_system &vfs, Env &env, + Xml_node const & config) + : + _env { env }, + _vfs { vfs }, + _vfs_handle { nullptr }, + _write_buffer { _env.ram(), _env.rm(), + config.attribute_value("write_buffer_size", 1u << 20) }, + _read_buffer { _env.ram(), _env.rm(), + config.attribute_value("read_buffer_size", 1u << 20) }, + _verbose { config.attribute_value("verbose", false) }, + _replay_node { config.sub_node("replay") } + { + Genode::memset(_write_buffer.local_addr(), 0x55, + _write_buffer.size()); + } + + void kick_off(Genode::Allocator &alloc, char const *file, + Genode::Signal_context_capability sigh_cap) + { + typedef Vfs::Directory_service::Open_result Open_result; + + Open_result res = _vfs.open(file, + Vfs::Directory_service::OPEN_MODE_RDWR, + &_vfs_handle, alloc); + if (res != Open_result::OPEN_OK) { + throw Genode::Exception(); + } + + _io_response_handler.sigh = sigh_cap; + + _vfs_handle->handler(&_io_response_handler); + + _current_request = { + .type = Request::Type::INVALID, + .state = Request::State::NONE, + .offset = 0, + .count = 0, + .out_count = 0, + .current_offset = 0, + .current_count = 0, + .success = false, + .complete = false, + }; + + _num_requests = _replay_node.num_sub_nodes(); + _request_node = _replay_node.sub_node(_curr_request_id); + _process_replay(); + } + + void io_progress_response_handler() + { + /* ignore any out-standing signal */ + if (_finished) { return; } + + _process_replay(); + } +}; + + +struct Main : private Genode::Entrypoint::Io_progress_handler +{ + Genode::Env &_env; + Genode::Heap _heap { _env.ram(), _env.rm() }; + + Genode::Attached_rom_dataspace _config_rom { _env, "config" }; + + Vfs::Simple_env _vfs_env { _env, _heap, _config_rom.xml().sub_node("vfs") }; + + Genode::Signal_handler
_reactivate_handler { + _env.ep(), *this, &Main::handle_io_progress }; + + Vfs_replay _replay { _vfs_env.root_dir(), _env, _config_rom.xml() }; + + Main(Genode::Env &env) : _env { env } + { + using File_name = Genode::String<64>; + File_name const file_name = + _config_rom.xml().attribute_value("file", File_name()); + if (!file_name.valid()) { + Genode::error("config 'file' attribute invalid"); + throw Genode::Exception(); + } + + _env.ep().register_io_progress_handler(*this); + + _replay.kick_off(_heap, file_name.string(), _reactivate_handler); + } + + void handle_io_progress() override + { + _replay.io_progress_response_handler(); + } +}; + + +void Component::construct(Genode::Env &env) +{ + static Main main(env); +} diff --git a/repos/gems/src/app/vfs_replay/target.mk b/repos/gems/src/app/vfs_replay/target.mk new file mode 100644 index 0000000000..63d8f78430 --- /dev/null +++ b/repos/gems/src/app/vfs_replay/target.mk @@ -0,0 +1,3 @@ +TARGET = cbe_vfs_replay +SRC_CC = component.cc +LIBS = base vfs diff --git a/repos/gems/src/lib/vfs/cbe/dummy.ads b/repos/gems/src/lib/vfs/cbe/dummy.ads new file mode 100644 index 0000000000..bf6121ad6e --- /dev/null +++ b/repos/gems/src/lib/vfs/cbe/dummy.ads @@ -0,0 +1,17 @@ +-- +-- \brief Integration of the Consistent Block Encrypter (CBE) +-- \author Martin Stein +-- \author Josef Soentgen +-- \date 2020-11-10 +-- + +-- +-- Copyright (C) 2020 Genode Labs GmbH +-- +-- This file is part of the Genode OS framework, which is distributed +-- under the terms of the GNU Affero General Public License version 3. +-- + +package Dummy +is +end Dummy; diff --git a/repos/gems/src/lib/vfs/cbe/io_job.h b/repos/gems/src/lib/vfs/cbe/io_job.h new file mode 100644 index 0000000000..8016ca39af --- /dev/null +++ b/repos/gems/src/lib/vfs/cbe/io_job.h @@ -0,0 +1,297 @@ +/* + * \brief Integration of the Consistent Block Encrypter (CBE) + * \author Martin Stein + * \author Josef Soentgen + * \date 2020-11-10 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _CBE_VFS__IO_JOB_ +#define _CBE_VFS__IO_JOB_ + +namespace Vfs_cbe { + + using file_size = Vfs::file_size; + using file_offset = Vfs::file_offset; + + struct Io_job + { + struct Unsupported_Operation : Genode::Exception { }; + struct Invalid_state : Genode::Exception { }; + + enum State { PENDING, IN_PROGRESS, COMPLETE, }; + + static State _initial_state(Cbe::Request::Operation const op) + { + using Op = Cbe::Request::Operation; + + switch (op) { + case Op::READ: return State::PENDING; + case Op::WRITE: return State::PENDING; + case Op::SYNC: return State::PENDING; + default: throw Unsupported_Operation(); + } + } + + static char const *_state_to_string(State s) + { + switch (s) { + case State::PENDING: return "PENDING"; + case State::IN_PROGRESS: return "IN_PROGRESS"; + case State::COMPLETE: return "COMPLETE"; + } + + throw Invalid_state(); + } + + Vfs::Vfs_handle &_handle; + + Cbe::Request::Operation const _op; + Cbe::Io_buffer::Index const _index; + State _state; + file_offset const _base_offset; + file_offset _current_offset; + file_size _current_count; + + bool _success; + bool _complete; + + bool _read(Cbe::Library &cbe, Cbe::Io_buffer &io_data) + { + bool progress = false; + + switch (_state) { + case State::PENDING: + + _handle.seek(_base_offset + _current_offset); + if (!_handle.fs().queue_read(&_handle, _current_count)) { + return progress; + } + + cbe.io_request_in_progress(_index); + + _state = State::IN_PROGRESS; + progress = true; + [[fallthrough]]; + case State::IN_PROGRESS: + { + using Result = Vfs::File_io_service::Read_result; + + bool completed = false; + file_size out = 0; + + char * const data = reinterpret_cast(&io_data.item(_index)); + + Result const result = + _handle.fs().complete_read(&_handle, + data + _current_offset, + _current_count, out); + if ( result == Result::READ_QUEUED + || result == Result::READ_ERR_INTERRUPT + || result == Result::READ_ERR_AGAIN + || result == Result::READ_ERR_WOULD_BLOCK) { + return progress; + } else + + if (result == Result::READ_OK) { + _current_offset += out; + _current_count -= out; + _success = true; + } else + + if ( result == Result::READ_ERR_IO + || result == Result::READ_ERR_INVALID) { + _success = false; + completed = true; + } + + if (_current_count == 0 || completed) { + _state = State::COMPLETE; + } else { + _state = State::PENDING; + /* partial read, keep trying */ + return true; + } + progress = true; + } + [[fallthrough]]; + case State::COMPLETE: + + cbe.io_request_completed(_index, _success); + _complete = true; + progress = true; + default: break; + } + + return progress; + } + + bool _write(Cbe::Library &cbe, Cbe::Io_buffer &io_data) + { + bool progress = false; + + switch (_state) { + case State::PENDING: + + _handle.seek(_base_offset + _current_offset); + + cbe.io_request_in_progress(_index); + _state = State::IN_PROGRESS; + progress = true; + [[fallthrough]]; + case State::IN_PROGRESS: + { + using Result = Vfs::File_io_service::Write_result; + + bool completed = false; + file_size out = 0; + + Result result = Result::WRITE_ERR_INVALID; + try { + char const * const data = + reinterpret_cast(&io_data.item(_index)); + result = _handle.fs().write(&_handle, + data + _current_offset, + _current_count, out); + } catch (Vfs::File_io_service::Insufficient_buffer) { + return progress; + } + + if ( result == Result::WRITE_ERR_AGAIN + || result == Result::WRITE_ERR_INTERRUPT + || result == Result::WRITE_ERR_WOULD_BLOCK) { + return progress; + } else + + if (result == Result::WRITE_OK) { + _current_offset += out; + _current_count -= out; + _success = true; + } else + + if ( result == Result::WRITE_ERR_IO + || result == Result::WRITE_ERR_INVALID) { + _success = false; + completed = true; + } + + if (_current_count == 0 || completed) { + _state = State::COMPLETE; + } else { + _state = State::PENDING; + /* partial write, keep trying */ + return true; + } + progress = true; + } + [[fallthrough]]; + case State::COMPLETE: + + cbe.io_request_completed(_index, _success); + _complete = true; + progress = true; + default: break; + } + + return progress; + } + + bool _sync(Cbe::Library &cbe, Cbe::Io_buffer &io_data) + { + bool progress = false; + + switch (_state) { + case State::PENDING: + + if (!_handle.fs().queue_sync(&_handle)) { + return progress; + } + cbe.io_request_in_progress(_index); + _state = State::IN_PROGRESS; + progress = true; + [[fallthrough]]; + case State::IN_PROGRESS: + { + using Result = Vfs::File_io_service::Sync_result; + Result const result = _handle.fs().complete_sync(&_handle); + + if (result == Result::SYNC_QUEUED) { + return progress; + } else + + if (result == Result::SYNC_ERR_INVALID) { + _success = false; + } else + + if (result == Result::SYNC_OK) { + _success = true; + } + + _state = State::COMPLETE; + progress = true; + } + [[fallthrough]]; + case State::COMPLETE: + + cbe.io_request_completed(_index, _success); + _complete = true; + progress = true; + default: break; + } + + return progress; + } + + Io_job(Vfs::Vfs_handle &handle, + Cbe::Request::Operation op, + Cbe::Io_buffer::Index index, + file_offset base_offset, + file_size length) + : + _handle { handle }, + _op { op }, + _index { index }, + _state { _initial_state(op) }, + _base_offset { base_offset }, + _current_offset { 0 }, + _current_count { length }, + _success { false }, + _complete { false } + { } + + bool completed() const { return _complete; } + bool succeeded() const { return _success; } + + void print(Genode::Output &out) const + { + Genode::print(out, "(", to_string(_op), ")", + " state: ", _state_to_string(_state), + " base_offset: ", _base_offset, + " current_offset: ", _current_offset, + " current_count: ", _current_count, + " success: ", _success, + " complete: ", _complete); + } + + bool execute(Cbe::Library &cbe, Cbe::Io_buffer &io_data) + { + using Op = Cbe::Request::Operation; + + switch (_op) { + case Op::READ: return _read(cbe, io_data); + case Op::WRITE: return _write(cbe, io_data); + case Op::SYNC: return _sync(cbe, io_data); + default: return false; + } + } + }; + +} /* namespace Vfs_cbe */ + +#endif /* _CBE_VFS__IO_JOB_ */ diff --git a/repos/gems/src/lib/vfs/cbe/target.mk b/repos/gems/src/lib/vfs/cbe/target.mk new file mode 100644 index 0000000000..7a941cbef7 --- /dev/null +++ b/repos/gems/src/lib/vfs/cbe/target.mk @@ -0,0 +1,3 @@ +TARGET := lib-vfs-cbe +REQUIRES = x86_64 +LIBS = vfs_cbe diff --git a/repos/gems/src/lib/vfs/cbe/vfs.cc b/repos/gems/src/lib/vfs/cbe/vfs.cc new file mode 100644 index 0000000000..03759d0d8d --- /dev/null +++ b/repos/gems/src/lib/vfs/cbe/vfs.cc @@ -0,0 +1,3119 @@ +/* + * \brief Integration of the Consistent Block Encrypter (CBE) + * \author Martin Stein + * \author Josef Soentgen + * \date 2020-11-10 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* CBE includes */ +#include +#include + +/* local includes */ +#include + + +namespace Vfs_cbe { + using namespace Vfs; + using namespace Genode; + + class Data_file_system; + + class Extend_file_system; + class Rekey_file_system; + class Create_snapshot_file_system; + class Discard_snapshot_file_system; + + struct Control_local_factory; + class Control_file_system; + + struct Snapshot_local_factory; + class Snapshot_file_system; + + struct Snapshots_local_factory; + class Snapshots_file_system; + + struct Local_factory; + class File_system; + + class Backend_file; + class Wrapper; +} + + +extern "C" void adainit(); + + +extern "C" void print_u8(unsigned char const u) { Genode::log(u); } + + +class Vfs_cbe::Wrapper +{ + private: + + Vfs::Env &_env; + + Vfs_handle *_backend_handle { nullptr }; + Constructible _backend_job { }; + + friend struct Backend_io_response_handler; + + struct Backend_io_response_handler : Vfs::Io_response_handler + { + Vfs_cbe::Wrapper &_wrapper; + Genode::Signal_context_capability _io_sigh; + + Backend_io_response_handler(Vfs_cbe::Wrapper &wrapper, + Genode::Signal_context_capability io_sigh) + : _wrapper(wrapper), _io_sigh(io_sigh) { } + + void read_ready_response() override { } + + void io_progress_response() override + { + if (_io_sigh.valid()) { + Genode::Signal_transmitter(_io_sigh).submit(); + } + } + }; + + Genode::Io_signal_handler _io_handler { + _env.env().ep(), *this, &Wrapper::_handle_io }; + + void _handle_io() + { + _notify_backend_io_progress(); + } + + void _notify_backend_io_progress() + { + if (_enqueued_vfs_handle) { + _enqueued_vfs_handle->io_progress_response(); + } else { + handle_frontend_request(); + _io_progress_pending = true; + } + } + + Backend_io_response_handler _backend_io_response_handler { *this, _io_handler }; + + Vfs_handle *_add_key_handle { nullptr }; + Vfs_handle *_remove_key_handle { nullptr }; + + struct Crypto_file + { + Vfs_handle *encrypt_handle; + Vfs_handle *decrypt_handle; + uint32_t key_id; + }; + + enum { NUM_CRYPTO_FILES = 2, }; + + Crypto_file _crypto_file[NUM_CRYPTO_FILES] { + { .encrypt_handle = nullptr, .decrypt_handle = nullptr, .key_id = 0 }, + { .encrypt_handle = nullptr, .decrypt_handle = nullptr, .key_id = 0 } }; + + Crypto_file *_get_unused_crypto_file() + { + for (Crypto_file &file : _crypto_file) { + if (file.key_id == 0) { + return &file; + } + } + struct No_unused_crypt_file_left { }; + throw No_unused_crypt_file_left(); + } + + Crypto_file *_lookup_crypto_file(uint32_t key_id) + { + for (Crypto_file &file : _crypto_file) { + if (file.key_id == key_id) { + return &file; + } + } + struct Crypt_file_not_found { }; + throw Crypt_file_not_found(); + } + + Cbe::Io_buffer _io_data { }; + Cbe::Crypto_cipher_buffer _cipher_data { }; + Cbe::Crypto_plain_buffer _plain_data { }; + + Constructible _trust_anchor { }; + + Constructible _cbe; + + public: + + struct Rekeying + { + enum State { UNKNOWN, IDLE, IN_PROGRESS, }; + enum Result { NONE, SUCCESS, FAILED, }; + State state; + Result last_result; + uint32_t key_id; + + static char const *state_to_cstring(State const s) + { + switch (s) { + case State::UNKNOWN: return "unknown"; + case State::IDLE: return "idle"; + case State::IN_PROGRESS: return "in-progress"; + } + + return "-"; + } + }; + + struct Extending + { + enum Type { INVALID, VBD, FT }; + enum State { UNKNOWN, IDLE, IN_PROGRESS, }; + enum Result { NONE, SUCCESS, FAILED, }; + Type type; + State state; + Result last_result; + + static char const *state_to_cstring(State const s) + { + switch (s) { + case State::UNKNOWN: return "unknown"; + case State::IDLE: return "idle"; + case State::IN_PROGRESS: return "in-progress"; + } + + return "-"; + } + + static Type string_to_type(char const *s) + { + if (Genode::strcmp("vbd", s, 3) == 0) { + return Type::VBD; + } else + + if (Genode::strcmp("ft", s, 2) == 0) { + return Type::FT; + } + + return Type::INVALID; + } + }; + + private: + + Rekeying _rekey_obj { + .state = Rekeying::State::UNKNOWN, + .last_result = Rekeying::Result::NONE, + .key_id = 0, }; + + Extending _extend_obj { + .type = Extending::Type::INVALID, + .state = Extending::State::UNKNOWN, + .last_result = Extending::Result::NONE, + }; + + /* configuration options */ + bool _verbose { false }; + bool _debug { false }; + + using Backend_device_path = Genode::String<32>; + Backend_device_path _block_device { "/dev/block" }; + + using Crypto_device_path = Genode::String<32>; + Crypto_device_path _crypto_device { "/dev/cbe_crypto" }; + + using Trust_anchor_device_path = Genode::String<64>; + Trust_anchor_device_path _trust_anchor_device { "/dev/cbe_trust_anchor" }; + + void _read_config(Xml_node config) + { + _verbose = config.attribute_value("verbose", _verbose); + _debug = config.attribute_value("debug", _debug); + _block_device = config.attribute_value("block", _block_device); + + _crypto_device = config.attribute_value("crypto", _crypto_device); + + _trust_anchor_device = + config.attribute_value("trust_anchor", _trust_anchor_device); + } + + struct Could_not_open_block_backend : Genode::Exception { }; + struct No_valid_superblock_found : Genode::Exception { }; + + void _initialize_cbe() + { + using Result = Vfs::Directory_service::Open_result; + + Result res = _env.root_dir().open(_block_device.string(), + Vfs::Directory_service::OPEN_MODE_RDWR, + (Vfs::Vfs_handle **)&_backend_handle, + _env.alloc()); + if (res != Result::OPEN_OK) { + error("cbe_fs: Could not open back end block device: '", _block_device, "'"); + throw Could_not_open_block_backend(); + } + + _backend_handle->handler(&_backend_io_response_handler); + + { + Genode::String<128> crypto_add_key_file { + _crypto_device.string(), "/add_key" }; + + res = _env.root_dir().open(crypto_add_key_file.string(), + Vfs::Directory_service::OPEN_MODE_WRONLY, + (Vfs::Vfs_handle **)&_add_key_handle, + _env.alloc()); + if (res != Result::OPEN_OK) { + error("cbe_fs: Could not open '", crypto_add_key_file, "' file"); + throw Could_not_open_block_backend(); + } + } + + { + Genode::String<128> crypto_remove_key_file { + _crypto_device.string(), "/remove_key" }; + + res = _env.root_dir().open(crypto_remove_key_file.string(), + Vfs::Directory_service::OPEN_MODE_WRONLY, + (Vfs::Vfs_handle **)&_remove_key_handle, + _env.alloc()); + if (res != Result::OPEN_OK) { + error("cbe_fs: Could not open '", crypto_remove_key_file, "' file"); + throw Could_not_open_block_backend(); + } + } + + _trust_anchor.construct(_env.root_dir(), _env.alloc(), + _trust_anchor_device.string(), + _io_handler); + + _cbe.construct(); + } + + + public: + + Wrapper(Vfs::Env &env, Xml_node config) : _env(env) + { + _read_config(config); + _initialize_cbe(); + } + + Cbe::Library &cbe() + { + if (!_cbe.constructed()) { + struct Cbe_Not_Initialized { }; + throw Cbe_Not_Initialized(); + } + + return *_cbe; + } + + struct Invalid_Request : Genode::Exception { }; + + struct Helper_request + { + enum { BLOCK_SIZE = 512, }; + enum State { NONE, PENDING, IN_PROGRESS, COMPLETE, ERROR }; + + State state { NONE }; + + Cbe::Block_data block_data { }; + Cbe::Request cbe_request { }; + + bool pending() const { return state == PENDING; } + bool in_progress() const { return state == IN_PROGRESS; } + bool complete() const { return state == COMPLETE; } + }; + + Helper_request _helper_read_request { }; + Helper_request _helper_write_request { }; + + struct Frontend_request + { + enum State { + NONE, + PENDING, IN_PROGRESS, COMPLETE, + ERROR, ERROR_EOF + }; + State state { NONE }; + file_size count { 0 }; + Cbe::Request cbe_request { }; + uint32_t snap_id { 0 }; + + uint64_t offset { 0 }; + uint64_t helper_offset { 0 }; + + bool pending() const { return state == PENDING; } + bool in_progress() const { return state == IN_PROGRESS; } + bool complete() const { return state == COMPLETE; } + + static char const *state_to_string(State s) + { + switch (s) { + case State::NONE: return "NONE"; + case State::PENDING: return "PENDING"; + case State::IN_PROGRESS: return "IN_PROGRESS"; + case State::COMPLETE: return "COMPLETE"; + case State::ERROR: return "ERROR"; + case State::ERROR_EOF: return "ERROR_EOF"; + } + return ""; + } + }; + + Frontend_request _frontend_request { }; + + Frontend_request const & frontend_request() const + { + return _frontend_request; + } + + // XXX needs to be a list when snapshots are used + Vfs_handle *_enqueued_vfs_handle { nullptr }; + bool _io_progress_pending { false }; + + void enqueue_handle(Vfs_handle &handle) + { + _enqueued_vfs_handle = &handle; + if (_io_progress_pending) { + _enqueued_vfs_handle->io_progress_response(); + _io_progress_pending = false; + } + } + + void ack_frontend_request(Vfs_handle &handle) + { + // assert current state was *_COMPLETE + _frontend_request.state = Frontend_request::State::NONE; + _frontend_request.cbe_request = Cbe::Request { }; + _enqueued_vfs_handle = nullptr; + } + + bool submit_frontend_request(Vfs_handle &handle, + char *data, + file_size count, + Cbe::Request::Operation op, + uint32_t snap_id) + { + if (_frontend_request.state != Frontend_request::State::NONE) { + return false; + } + + /* short-cut for SYNC requests */ + if (op == Cbe::Request::Operation::SYNC) { + _frontend_request.cbe_request = Cbe::Request( + op, + false, + 0, + 0, + 1, + 0, + 0); + _frontend_request.count = 0; + _frontend_request.snap_id = 0; + _frontend_request.state = Frontend_request::State::PENDING; + if (_verbose) { + Genode::log("Req: (front req: ", + _frontend_request.cbe_request, ")"); + } + return true; + } + + file_size const offset = handle.seek(); + bool unaligned_request = false; + + /* unaligned request if any condition is true */ + unaligned_request |= (offset % Cbe::BLOCK_SIZE) != 0; + unaligned_request |= (count < Cbe::BLOCK_SIZE); + + if ((count % Cbe::BLOCK_SIZE) != 0 && + !unaligned_request) + { + count = count - (count % Cbe::BLOCK_SIZE); + } + + if (unaligned_request) { + _helper_read_request.cbe_request = Cbe::Request( + Cbe::Request::Operation::READ, + false, + offset / Cbe::BLOCK_SIZE, + (uint64_t)&_helper_read_request.block_data, + 1, + 0, + 0); + _helper_read_request.state = Helper_request::State::PENDING; + + _frontend_request.helper_offset = (offset % Cbe::BLOCK_SIZE); + if (count >= (Cbe::BLOCK_SIZE - _frontend_request.helper_offset)) { + _frontend_request.count = Cbe::BLOCK_SIZE - _frontend_request.helper_offset; + } else { + _frontend_request.count = count; + } + + /* skip handling by the CBE, helper requests will do that for us */ + _frontend_request.state = Frontend_request::State::IN_PROGRESS; + + } else { + _frontend_request.count = count; + _frontend_request.state = Frontend_request::State::PENDING; + } + + _frontend_request.offset = offset; + _frontend_request.cbe_request = Cbe::Request( + op, + false, + offset / Cbe::BLOCK_SIZE, + (uint64_t)data, + (uint32_t)(count / Cbe::BLOCK_SIZE), + 0, + 0); + + if (_verbose) { + if (unaligned_request) { + Genode::log("Unaligned req: ", + "off: ", offset, " bytes: ", count, + " (front req: ", _frontend_request.cbe_request, + " (helper req: ", _helper_read_request.cbe_request, + " off: ", _frontend_request.helper_offset, + " count: ", _frontend_request.count, ")"); + } else { + Genode::log("Req: ", + "off: ", offset, " bytes: ", count, + " (front req: ", _frontend_request.cbe_request, ")"); + } + } + + _frontend_request.snap_id = snap_id; + return true; + } + + bool _handle_cbe_backend(Cbe::Library &cbe, Cbe::Io_buffer &io_data) + { + Cbe::Io_buffer::Index data_index { 0 }; + Cbe::Request cbe_request = cbe.has_io_request(data_index); + + if (cbe_request.valid() && !_backend_job.constructed()) { + + file_offset const base_offset = cbe_request.block_number() + * Cbe::BLOCK_SIZE; + file_size const count = cbe_request.count() + * Cbe::BLOCK_SIZE; + + _backend_job.construct(*_backend_handle, cbe_request.operation(), + data_index, base_offset, count); + } + + if (!_backend_job.constructed()) { + return false; + } + + bool progress = _backend_job->execute(cbe, io_data); + + if (_backend_job->completed()) { + _backend_job.destruct(); + } + + return progress; + } + + bool _handle_cbe_frontend(Cbe::Library &cbe, Frontend_request &frontend_request) + { + if (_helper_read_request.pending()) { + if (_cbe->client_request_acceptable()) { + _cbe->submit_client_request(_helper_read_request.cbe_request, + frontend_request.snap_id); + _helper_read_request.state = Helper_request::State::IN_PROGRESS; + } + } + + if (_helper_write_request.pending()) { + if (_cbe->client_request_acceptable()) { + _cbe->submit_client_request(_helper_write_request.cbe_request, + frontend_request.snap_id); + _helper_write_request.state = Helper_request::State::IN_PROGRESS; + } + } + + if (frontend_request.pending()) { + + using ST = Frontend_request::State; + + Cbe::Request const &request = frontend_request.cbe_request; + Cbe::Virtual_block_address const vba = request.block_number(); + uint32_t const snap_id = frontend_request.snap_id; + + if (vba > cbe.max_vba()) { + warning("reject request with out-of-range virtual block start address ", vba); + _frontend_request.state = ST::ERROR_EOF; + return false; + } + + if (vba + request.count() < vba) { + warning("reject wraping request", vba); + _frontend_request.state = ST::ERROR_EOF; + return false; + } + + if (vba + request.count() > (cbe.max_vba() + 1)) { + warning("reject invalid request ", vba, " ", request.count()); + _frontend_request.state = ST::ERROR_EOF; + return false; + } + + if (cbe.client_request_acceptable()) { + cbe.submit_client_request(request, snap_id); + frontend_request.state = ST::IN_PROGRESS; + } + } + + cbe.execute(_io_data, _plain_data, _cipher_data); + bool progress = cbe.execute_progress(); + + using ST = Frontend_request::State; + + while (true) { + Cbe::Request const cbe_request = cbe.peek_completed_client_request(); + if (!cbe_request.valid()) { break; } + + cbe.drop_completed_client_request(cbe_request); + progress = true; + + if (cbe_request.operation() == Cbe::Request::Operation::REKEY) { + bool const req_sucess = cbe_request.success(); + if (_verbose) { + log("Complete request: backend request (", cbe_request, ")"); + } + _rekey_obj.state = Rekeying::State::IDLE; + _rekey_obj.last_result = req_sucess ? Rekeying::Result::SUCCESS + : Rekeying::Result::FAILED; + continue; + } + + if (cbe_request.operation() == Cbe::Request::Operation::EXTEND_VBD) { + bool const req_sucess = cbe_request.success(); + if (_verbose) { + log("Complete request: backend request (", cbe_request, ")"); + } + _extend_obj.state = Extending::State::IDLE; + _extend_obj.last_result = + req_sucess ? Extending::Result::SUCCESS + : Extending::Result::FAILED; + continue; + } + + if (cbe_request.operation() == Cbe::Request::Operation::EXTEND_FT) { + bool const req_sucess = cbe_request.success(); + if (_verbose) { + log("Complete request: backend request (", cbe_request, ")"); + } + _extend_obj.state = Extending::State::IDLE; + _extend_obj.last_result = + req_sucess ? Extending::Result::SUCCESS + : Extending::Result::FAILED; + continue; + } + + if (cbe_request.operation() == Cbe::Request::Operation::CREATE_SNAPSHOT) { + if (_verbose) { + log("Complete request: (", cbe_request, ")"); + } + _create_snapshot_request.cbe_request = Cbe::Request(); + continue; + } + + if (cbe_request.operation() == Cbe::Request::Operation::DISCARD_SNAPSHOT) { + if (_verbose) { + log("Complete request: (", cbe_request, ")"); + } + _discard_snapshot_request.cbe_request = Cbe::Request(); + continue; + } + + if (!cbe_request.success()) { + _helper_read_request.state = Helper_request::State::NONE; + _helper_write_request.state = Helper_request::State::NONE; + + frontend_request.state = ST::COMPLETE; + frontend_request.cbe_request.success(cbe_request.success()); + break; + } + + if (_helper_read_request.in_progress()) { + _helper_read_request.state = Helper_request::State::COMPLETE; + _helper_read_request.cbe_request.success( + cbe_request.success()); + } else if (_helper_write_request.in_progress()) { + _helper_write_request.state = Helper_request::State::COMPLETE; + _helper_write_request.cbe_request.success( + cbe_request.success()); + } else { + frontend_request.state = ST::COMPLETE; + frontend_request.cbe_request.success(cbe_request.success()); + if (_verbose) { + Genode::log("Complete request: ", + " (frontend request: ", _frontend_request.cbe_request, + " count: ", _frontend_request.count, ")"); + } + } + } + + if (_helper_read_request.complete()) { + if (frontend_request.cbe_request.read()) { + char * dst = reinterpret_cast + (frontend_request.cbe_request.offset()); + char const * src = reinterpret_cast + (&_helper_read_request.block_data) + frontend_request.helper_offset; + + Genode::memcpy(dst, src, _frontend_request.count); + + _helper_read_request.state = Helper_request::State::NONE; + frontend_request.state = ST::COMPLETE; + frontend_request.cbe_request.success( + _helper_read_request.cbe_request.success()); + + if (_verbose) { + Genode::log("Complete unaligned READ request: ", + " (frontend request: ", _frontend_request.cbe_request, + " (helper request: ", _helper_read_request.cbe_request, + " offset: ", _frontend_request.helper_offset, + " count: ", _frontend_request.count, ")"); + } + } + + if (frontend_request.cbe_request.write()) { + /* copy whole block first */ + { + char * dst = reinterpret_cast + (&_helper_write_request.block_data); + char const * src = reinterpret_cast + (&_helper_read_request.block_data); + Genode::memcpy(dst, src, sizeof (Cbe::Block_data)); + } + + /* and than actual request data */ + { + char * dst = reinterpret_cast + (&_helper_write_request.block_data) + frontend_request.helper_offset; + char const * src = reinterpret_cast + (frontend_request.cbe_request.offset()); + Genode::memcpy(dst, src, _frontend_request.count); + } + + /* re-use request */ + _helper_write_request.cbe_request = Cbe::Request( + Cbe::Request::Operation::WRITE, + false, + _helper_read_request.cbe_request.block_number(), + (uint64_t) &_helper_write_request.block_data, + _helper_read_request.cbe_request.count(), + _helper_read_request.cbe_request.key_id(), + _helper_read_request.cbe_request.tag()); + + _helper_write_request.state = Helper_request::State::PENDING; + _helper_read_request.state = Helper_request::State::NONE; + } + progress = true; + } + + if (_helper_write_request.complete()) { + if (_verbose) { + Genode::log("Complete unaligned WRITE request: ", + " (frontend request: ", _frontend_request.cbe_request, + " (helper request: ", _helper_read_request.cbe_request, + " offset: ", _frontend_request.helper_offset, + " count: ", _frontend_request.count, ")"); + } + + _helper_write_request.state = Helper_request::State::NONE; + frontend_request.state = ST::COMPLETE; + progress = true; + } + + /* read */ + { + struct Read_data_pointer_is_null : Genode::Exception { }; + struct Front_end_read_request_should_be_in_progress : + Genode::Exception { }; + + Cbe::Request cbe_req { }; + uint64_t vba { 0 }; + Cbe::Crypto_plain_buffer::Index plain_buf_idx { 0 }; + + _cbe->client_transfer_read_data_required( + cbe_req, vba, plain_buf_idx); + + if (cbe_req.valid()) { + + Cbe::Block_data *data { nullptr }; + + if (_helper_read_request.in_progress()) { + data = reinterpret_cast( + &_helper_read_request.block_data); + } else { + + if (_frontend_request.in_progress()) { + // XXX check after helper request because it will be IN_PROGRESS + // in case helper request is used + + uint64_t buf_base { cbe_req.offset() }; + uint64_t blk_off { vba - cbe_req.block_number() }; + data = reinterpret_cast( + buf_base + (blk_off * Cbe::BLOCK_SIZE)); + + } else { + throw Front_end_read_request_should_be_in_progress(); + } + } + if (data == nullptr) { + throw Read_data_pointer_is_null(); + } + Genode::memcpy( + data, + &_plain_data.item(plain_buf_idx), + sizeof (Cbe::Block_data)); + + _cbe->client_transfer_read_data_in_progress( + plain_buf_idx); + + _cbe->client_transfer_read_data_completed( + plain_buf_idx, true); + + progress = true; + } + } + + /* write */ + { + struct Write_data_pointer_is_null : Genode::Exception { }; + struct Front_end_write_request_should_be_in_progress : + Genode::Exception { }; + + Cbe::Request cbe_req { }; + uint64_t vba { 0 }; + Cbe::Crypto_plain_buffer::Index plain_buf_idx { 0 }; + + _cbe->client_transfer_write_data_required( + cbe_req, vba, plain_buf_idx); + + if (cbe_req.valid()) { + + Cbe::Block_data *data { nullptr }; + + if (_helper_write_request.in_progress()) { + data = reinterpret_cast( + &_helper_write_request.block_data); + } else { + + if (_frontend_request.in_progress()) { + // XXX check after helper request because it will be IN_PROGRESS + // in case helper request is used + + uint64_t buf_base { cbe_req.offset() }; + uint64_t blk_off { vba - cbe_req.block_number() }; + data = reinterpret_cast( + buf_base + (blk_off * Cbe::BLOCK_SIZE)); + } else { + throw Front_end_write_request_should_be_in_progress(); + } + } + if (data == nullptr) { + throw Write_data_pointer_is_null(); + } + Genode::memcpy( + &_plain_data.item(plain_buf_idx), + data, + sizeof (Cbe::Block_data)); + + _cbe->client_transfer_write_data_in_progress( + plain_buf_idx); + + _cbe->client_transfer_write_data_completed( + plain_buf_idx, true); + + progress = true; + } + } + + return progress; + } + + bool _handle_ta(Cbe::Library &cbe) + { + bool progress = false; + + Util::Trust_anchor_vfs &ta = *_trust_anchor; + + progress |= ta.execute(); + + using Op = Cbe::Trust_anchor_request::Operation; + + while (true) { + + Cbe::Trust_anchor_request const request = + _cbe->peek_generated_ta_request(); + + if (!request.valid()) { break; } + if (!ta.request_acceptable()) { break; } + + switch (request.operation()) { + case Op::CREATE_KEY: + ta.submit_create_key_request(request); + break; + case Op::SECURE_SUPERBLOCK: + { + Cbe::Hash const sb_hash = _cbe->peek_generated_ta_sb_hash(request); + ta.submit_secure_superblock_request(request, sb_hash); + break; + } + case Op::ENCRYPT_KEY: + { + Cbe::Key_plaintext_value const pk = + _cbe->peek_generated_ta_key_value_plaintext(request); + + ta.submit_encrypt_key_request(request, pk); + break; + } + case Op::DECRYPT_KEY: + { + Cbe::Key_ciphertext_value const ck = + _cbe->peek_generated_ta_key_value_ciphertext(request); + + ta.submit_decrypt_key_request(request, ck); + break; + } + case Op::LAST_SB_HASH: + ta.submit_superblock_hash_request(request); + break; + case Op::INITIALIZE: + class Bad_operation { }; + throw Bad_operation { }; + case Op::INVALID: + /* never reached */ + break; + } + _cbe->drop_generated_ta_request(request); + progress |= true; + } + + while (true) { + + Cbe::Trust_anchor_request const request = + ta.peek_completed_request(); + + if (!request.valid()) { break; } + + switch (request.operation()) { + case Op::CREATE_KEY: + { + Cbe::Key_plaintext_value const pk = + ta.peek_completed_key_value_plaintext(request); + + _cbe->mark_generated_ta_create_key_request_complete(request, pk); + break; + } + case Op::SECURE_SUPERBLOCK: + { + _cbe->mark_generated_ta_secure_sb_request_complete(request); + break; + } + case Op::ENCRYPT_KEY: + { + Cbe::Key_ciphertext_value const ck = + ta.peek_completed_key_value_ciphertext(request); + + _cbe->mark_generated_ta_encrypt_key_request_complete(request, ck); + break; + } + case Op::DECRYPT_KEY: + { + Cbe::Key_plaintext_value const pk = + ta.peek_completed_key_value_plaintext(request); + + _cbe->mark_generated_ta_decrypt_key_request_complete(request, pk); + break; + } + case Op::LAST_SB_HASH: + { + Cbe::Hash const hash = + ta.peek_completed_superblock_hash(request); + + _cbe->mark_generated_ta_last_sb_hash_request_complete(request, hash); + break; + } + case Op::INITIALIZE: + class Bad_operation { }; + throw Bad_operation { }; + case Op::INVALID: + /* never reached */ + break; + } + ta.drop_completed_request(request); + progress |= true; + } + + return progress; + } + + bool _handle_crypto_add_key() + { + bool progress = false; + + do { + Cbe::Key key { }; + Cbe::Request request = _cbe->crypto_add_key_required(key); + if (!request.valid()) { + break; + } + + char buffer[sizeof (key.value) + sizeof (key.id.value)] { }; + + memcpy(buffer, &key.id.value, sizeof (key.id.value)); + memcpy(buffer + sizeof (key.id.value), key.value, sizeof (key.value)); + + file_size written = 0; + _add_key_handle->seek(0); + try { + _add_key_handle->fs().write(_add_key_handle, + buffer, sizeof (buffer), written); + (void)written; + } catch (Vfs::File_io_service::Insufficient_buffer) { + /* try again later */ + break; + } + + /* + * Instead of acknowledge the CBE's request before we write + * the key above do it afterwards. That allows us to perform the + * request multiple times in case the above exception is thrown. + */ + _cbe->crypto_add_key_requested(request); + + uint32_t const key_id_value = key.id.value; + + Crypto_file *cf = nullptr; + try { + cf = _get_unused_crypto_file(); + } catch (...) { + error("cannot manage key id: ", key_id_value); + request.success(false); + _cbe->crypto_add_key_completed(request); + continue; + } + + using Encrypt_file = Genode::String<128>; + Encrypt_file encrypt_file { + _crypto_device.string(), "/keys/", key_id_value, "/encrypt" }; + + using Result = Vfs::Directory_service::Open_result; + Result res = _env.root_dir().open(encrypt_file.string(), + Vfs::Directory_service::OPEN_MODE_RDWR, + (Vfs::Vfs_handle **)&cf->encrypt_handle, + _env.alloc()); + + request.success(res == Result::OPEN_OK); + if (!request.success()) { + error("could not open encrypt '", encrypt_file, "' file for key id: ", key_id_value); + + request.success(false); + _cbe->crypto_add_key_completed(request); + continue; + } + + using Decrypt_file = Genode::String<128>; + Decrypt_file decrypt_file { + _crypto_device.string(), "/keys/", key_id_value, "/decrypt" }; + + res = _env.root_dir().open(decrypt_file.string(), + Vfs::Directory_service::OPEN_MODE_RDWR, + (Vfs::Vfs_handle **)&cf->decrypt_handle, + _env.alloc()); + + request.success(res == Result::OPEN_OK); + if (!request.success()) { + _env.root_dir().close(cf->encrypt_handle); + + error("could not open decrypt '", decrypt_file, "' file for key id: ", key_id_value); + + request.success(false); + _cbe->crypto_add_key_completed(request); + continue; + } + + /* set key id to make file valid */ + cf->key_id = key_id_value; + cf->encrypt_handle->handler(&_backend_io_response_handler); + cf->decrypt_handle->handler(&_backend_io_response_handler); + + request.success(true); + _cbe->crypto_add_key_completed(request); + progress |= true; + } while (false); + + return progress; + } + + bool _handle_crypto_remove_key() + { + bool progress = false; + + do { + Cbe::Key::Id key_id { }; + Cbe::Request request = _cbe->crypto_remove_key_required(key_id); + if (!request.valid()) { + break; + } + + file_size written = 0; + _remove_key_handle->seek(0); + try { + _remove_key_handle->fs().write(_remove_key_handle, + (char const*)&key_id.value, + sizeof (key_id.value), + written); + (void)written; + } catch (Vfs::File_io_service::Insufficient_buffer) { + /* try again later */ + break; + } + + Crypto_file *cf = nullptr; + try { + cf = _lookup_crypto_file(key_id.value); + + _env.root_dir().close(cf->encrypt_handle); + cf->encrypt_handle = nullptr; + _env.root_dir().close(cf->decrypt_handle); + cf->decrypt_handle = nullptr; + cf->key_id = 0; + } catch (...) { + uint32_t const key_id_value = key_id.value; + Genode::warning("could not look up handles for key id: ", + key_id_value); + } + + /* + * Instead of acknowledge the CBE's request before we write + * the key do it afterwards. That allows us to perform the + * request multiple times in case the above exception is thrown. + */ + _cbe->crypto_remove_key_requested(request); + request.success(true); + _cbe->crypto_remove_key_completed(request); + progress |= true; + } while (false); + + return progress; + } + + struct Crypto_job + { + struct Invalid_operation : Genode::Exception { }; + + enum State { IDLE, SUBMITTED, PENDING, IN_PROGRESS, COMPLETE }; + enum Operation { INVALID, DECRYPT, ENCRYPT }; + + Crypto_file *file; + Vfs_handle *_handle; + + State state; + Operation op; + uint32_t data_index; + file_offset offset; + + Cbe::Crypto_cipher_buffer::Index cipher_index; + Cbe::Crypto_plain_buffer::Index plain_index; + + static bool _read_queued(Vfs::File_io_service::Read_result r) + { + using Result = Vfs::File_io_service::Read_result; + switch (r) { + case Result::READ_QUEUED: [[fallthrough]]; + case Result::READ_ERR_INTERRUPT: [[fallthrough]]; + case Result::READ_ERR_AGAIN: [[fallthrough]]; + case Result::READ_ERR_WOULD_BLOCK: return true; + default: break; + } + return false; + } + + struct Result + { + bool progress; + bool complete; + bool success; + }; + + bool request_acceptable() const + { + return state == State::IDLE; + } + + template + void submit_request(Crypto_file *cf, uint32_t data_index, file_offset offset) + { + file = cf; + state = Crypto_job::State::SUBMITTED; + op = OP; + data_index = data_index; + offset = offset; + + /* store both in regardless of operation */ + cipher_index.value = data_index; + plain_index.value = data_index; + + switch (op) { + case Crypto_job::Operation::ENCRYPT: + _handle = cf->encrypt_handle; break; + case Crypto_job::Operation::DECRYPT: + _handle = cf->decrypt_handle; break; + case Crypto_job::Operation::INVALID: + throw Invalid_operation(); + break; + } + } + + Result execute(Cbe::Library &cbe, + Cbe::Crypto_cipher_buffer &cipher, + Cbe::Crypto_plain_buffer &plain) + { + Result result { false, false, false }; + + switch (state) { + case Crypto_job::State::IDLE: + break; + case Crypto_job::State::SUBMITTED: + try { + char const *data = nullptr; + + if (op == Crypto_job::Operation::ENCRYPT) { + data = reinterpret_cast(&plain.item(plain_index)); + } else + + if (op == Crypto_job::Operation::DECRYPT) { + data = reinterpret_cast(&cipher.item(cipher_index)); + } + + file_size out = 0; + _handle->seek(offset); + _handle->fs().write(_handle, data, + file_size(sizeof (Cbe::Block_data)), out); + + if (op == Crypto_job::Operation::ENCRYPT) { + cbe.crypto_cipher_data_requested(plain_index); + } else + + if (op == Crypto_job::Operation::DECRYPT) { + cbe.crypto_plain_data_requested(cipher_index); + } + + state = Crypto_job::State::PENDING; + result.progress |= true; + } catch (Vfs::File_io_service::Insufficient_buffer) { } + + [[fallthrough]]; + + case Crypto_job::State::PENDING: + + _handle->seek(offset); + if (!_handle->fs().queue_read(_handle, sizeof (Cbe::Block_data))) { + break; + } + + state = Crypto_job::State::IN_PROGRESS; + result.progress |= true; + [[fallthrough]]; + + case Crypto_job::State::IN_PROGRESS: + { + using Result = Vfs::File_io_service::Read_result; + + file_size out = 0; + char *data = nullptr; + + if (op == Crypto_job::Operation::ENCRYPT) { + data = reinterpret_cast(&cipher.item(cipher_index)); + } else + + if (op == Crypto_job::Operation::DECRYPT) { + data = reinterpret_cast(&plain.item(plain_index)); + } + + Result const res = + _handle->fs().complete_read(_handle, data, + sizeof (Cbe::Block_data), out); + if (_read_queued(res)) { + break; + } + + result.success = res == Result::READ_OK; + + state = Crypto_job::State::COMPLETE; + result.progress |= true; + [[fallthrough]]; + } + case Crypto_job::State::COMPLETE: + + if (op == Crypto_job::Operation::ENCRYPT) { + if (!result.success) { + error("encryption request failed"); // XXX be more informative + } + + cbe.supply_crypto_cipher_data(cipher_index, result.success); + } else + + if (op == Crypto_job::Operation::DECRYPT) { + if (!result.success) { + error("decryption request failed"); // XXX be more informative + } + + cbe.supply_crypto_plain_data(plain_index, result.success); + } + + state = Crypto_job::State::IDLE; + result.complete |= true; + result.progress |= true; + break; + } + + return result; + } + }; + + Crypto_job _crypto_job { nullptr, nullptr, Crypto_job::State::IDLE, + Crypto_job::Operation::INVALID, + 0 , 0, + Cbe::Crypto_cipher_buffer::Index { 0 }, + Cbe::Crypto_plain_buffer::Index { 0 } }; // XXX make that more than 1 job + + bool _handle_crypto_request(Cbe::Library &cbe, + Cbe::Crypto_cipher_buffer &cipher, + Cbe::Crypto_plain_buffer &plain) + { + bool progress = false; + + /* encrypt */ + while (true) { + + Cbe::Crypto_plain_buffer::Index data_index { 0 }; + Cbe::Request request = cbe.crypto_cipher_data_required(data_index); + if (!request.valid() || !_crypto_job.request_acceptable()) { + break; + } + + Crypto_file *cf = nullptr; + try { + cf = _lookup_crypto_file(request.key_id()); + } catch (...) { + cbe.crypto_cipher_data_requested(data_index); + + Cbe::Crypto_cipher_buffer::Index const index { data_index.value }; + cbe.supply_crypto_cipher_data(index, false); + continue; + } + + using Op = Crypto_job::Operation; + file_offset const offset = request.block_number() * Cbe::BLOCK_SIZE; + _crypto_job.submit_request(cf, data_index.value, offset); + progress |= true; + } + + /* decrypt */ + while (true) { + + Cbe::Crypto_cipher_buffer::Index data_index { 0 }; + Cbe::Request request = cbe.crypto_plain_data_required(data_index); + if (!request.valid() || !_crypto_job.request_acceptable()) { + break; + } + + Crypto_file *cf = nullptr; + try { + cf = _lookup_crypto_file(request.key_id()); + } catch (...) { + cbe.crypto_plain_data_requested(data_index); + Cbe::Crypto_plain_buffer::Index const index { data_index.value }; + cbe.supply_crypto_plain_data(index, false); + continue; + } + + using Op = Crypto_job::Operation; + file_offset const offset = request.block_number() * Cbe::BLOCK_SIZE; + _crypto_job.submit_request(cf, data_index.value, offset); + progress |= true; + } + + Crypto_job::Result const result = _crypto_job.execute(cbe, cipher, plain); + progress |= result.progress; + + return progress; + } + + bool _handle_crypto() + { + bool progress = false; + + bool const add_key_progress = _handle_crypto_add_key(); + progress |= add_key_progress; + + bool const remove_key_progress = _handle_crypto_remove_key(); + progress |= remove_key_progress; + + bool const request_progress = _handle_crypto_request(*_cbe, _cipher_data, _plain_data); + progress |= request_progress; + + return progress; + } + + void _dump_state() + { + if (_debug) { + static uint64_t cnt = 0; + log("FE: ", Frontend_request::state_to_string(_frontend_request.state), + " (", _frontend_request.cbe_request, ") ", + "BE: ", *_backend_job, " ", ++cnt); + } + } + + void handle_frontend_request() + { + while (true) { + + bool progress = false; + + bool const frontend_progress = + _handle_cbe_frontend(*_cbe, _frontend_request); + progress |= frontend_progress; + + bool const backend_progress = _handle_cbe_backend(*_cbe, _io_data); + progress |= backend_progress; + + bool const crypto_progress = _handle_crypto(); + progress |= crypto_progress; + + bool const ta_progress = _handle_ta(*_cbe); + progress |= ta_progress; + + if (!progress) { + _dump_state(); + } + + if (_debug) { + log("frontend_progress: ", frontend_progress, + " backend_progress: ", backend_progress, + " crypto_progress: ", crypto_progress); + } + + if (!progress) { break; } + } + + Cbe::Info const info = _cbe->info(); + + using ES = Extending::State; + if (_extend_obj.state == ES::UNKNOWN && info.valid) { + if (info.extending_ft) { + _extend_obj.state = ES::IN_PROGRESS; + _extend_obj.type = Extending::Type::FT; + } else + + if (info.extending_vbd) { + _extend_obj.state = ES::IN_PROGRESS; + _extend_obj.type = Extending::Type::VBD; + } else { + _extend_obj.state = ES::IDLE; + } + } + using RS = Rekeying::State; + if (_rekey_obj.state == RS::UNKNOWN && info.valid) { + _rekey_obj.state = + info.rekeying ? RS::IN_PROGRESS : RS::IDLE; + } + } + + bool client_request_acceptable() const + { + return _cbe->client_request_acceptable(); + } + + bool start_rekeying() + { + if (!_cbe->client_request_acceptable()) { + return false; + } + + Cbe::Request req( + Cbe::Request::Operation::REKEY, + false, + 0, 0, 0, + _rekey_obj.key_id, + 0); + + if (_verbose) { + Genode::log("Req: (background req: ", req, ")"); + } + + _cbe->submit_client_request(req, 0); + _rekey_obj.state = Rekeying::State::IN_PROGRESS; + _rekey_obj.last_result = Rekeying::Rekeying::FAILED; + + // XXX kick-off rekeying + handle_frontend_request(); + return true; + } + + Rekeying const rekeying_progress() const + { + return _rekey_obj; + } + + + bool start_extending(Extending::Type type, + Cbe::Number_of_blocks blocks) + { + if (!_cbe->client_request_acceptable()) { + return false; + } + + Cbe::Request::Operation op = + Cbe::Request::Operation::INVALID; + + switch (type) { + case Extending::Type::VBD: + op = Cbe::Request::Operation::EXTEND_VBD; + break; + case Extending::Type::FT: + op = Cbe::Request::Operation::EXTEND_FT; + break; + case Extending::Type::INVALID: + return false; + } + + Cbe::Request req(op, false, + 0, 0, blocks, 0, 0); + + if (_verbose) { + Genode::log("Req: (background req: ", req, ")"); + } + + _cbe->submit_client_request(req, 0); + _extend_obj.type = type; + _extend_obj.state = Extending::State::IN_PROGRESS; + _extend_obj.last_result = Extending::Result::NONE; + + // XXX kick-off extending + handle_frontend_request(); + return true; + } + + Extending const extending_progress() const + { + return _extend_obj; + } + + void active_snapshot_ids(Cbe::Active_snapshot_ids &ids) + { + if (!_cbe.constructed()) { + _initialize_cbe(); + } + _cbe->active_snapshot_ids(ids); + handle_frontend_request(); + } + + + Frontend_request _create_snapshot_request { }; + + bool create_snapshot() + { + if (!_cbe.constructed()) { + _initialize_cbe(); + } + + if (!_cbe->client_request_acceptable()) { + return false; + } + + if (_create_snapshot_request.cbe_request.valid()) { + return false; + } + + Cbe::Request::Operation const op = + Cbe::Request::Operation::CREATE_SNAPSHOT; + + _create_snapshot_request.cbe_request = + Cbe::Request(op, false, 0, 0, 1, 0, 0); + + if (_verbose) { + Genode::log("Req: (req: ", _create_snapshot_request.cbe_request, ")"); + } + + _cbe->submit_client_request(_create_snapshot_request.cbe_request, 0); + + _create_snapshot_request.state = + Frontend_request::State::IN_PROGRESS; + + // XXX kick-off snapshot creation request + handle_frontend_request(); + return true; + } + + Frontend_request _discard_snapshot_request { }; + + bool discard_snapshot(Cbe::Generation id) + { + if (!_cbe.constructed()) { + _initialize_cbe(); + } + + if (!_cbe->client_request_acceptable()) { + return false; + } + + if (_discard_snapshot_request.cbe_request.valid()) { + return false; + } + + Cbe::Request::Operation const op = + Cbe::Request::Operation::DISCARD_SNAPSHOT; + + _discard_snapshot_request.cbe_request = + Cbe::Request(op, false, 0, 0, 1, 0, 0); + + if (_verbose) { + Genode::log("Req: (req: ", _discard_snapshot_request.cbe_request, ")"); + } + + _cbe->submit_client_request(_discard_snapshot_request.cbe_request, 0); + + _discard_snapshot_request.state = + Frontend_request::State::IN_PROGRESS; + + // XXX kick-off snapshot creation request + handle_frontend_request(); + return true; + } + + Genode::Mutex _frontend_mtx { }; + + Genode::Mutex &frontend_mtx() { return _frontend_mtx; } +}; + + +class Vfs_cbe::Data_file_system : public Single_file_system +{ + private: + + Wrapper &_w; + uint32_t _snap_id; + + public: + + struct Vfs_handle : Single_vfs_handle + { + Wrapper &_w; + uint32_t _snap_id { 0 }; + + Vfs_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Wrapper &w, + uint32_t snap_id) + : + Single_vfs_handle(ds, fs, alloc, 0), + _w(w), _snap_id(snap_id) + { } + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + Genode::Mutex::Guard guard { _w.frontend_mtx() }; + + using State = Wrapper::Frontend_request::State; + + State state = _w.frontend_request().state; + if (state == State::NONE) { + + if (!_w.client_request_acceptable()) { + return READ_QUEUED; + } + using Op = Cbe::Request::Operation; + + bool const accepted = + _w.submit_frontend_request(*this, dst, count, + Op::READ, _snap_id); + if (!accepted) { return READ_ERR_IO; } + } + + _w.handle_frontend_request(); + state = _w.frontend_request().state; + + if ( state == State::PENDING + || state == State::IN_PROGRESS) { + _w.enqueue_handle(*this); + return READ_QUEUED; + } + + if (state == State::COMPLETE) { + out_count = _w.frontend_request().count; + _w.ack_frontend_request(*this); + return READ_OK; + } + + if (state == State::ERROR_EOF) { + out_count = 0; + _w.ack_frontend_request(*this); + return READ_OK; + } + + if (state == State::ERROR) { + out_count = 0; + _w.ack_frontend_request(*this); + return READ_ERR_IO; + } + + return READ_ERR_IO; + } + + Write_result write(char const *src, file_size count, + file_size &out_count) override + { + Genode::Mutex::Guard guard { _w.frontend_mtx() }; + + using State = Wrapper::Frontend_request::State; + + State state = _w.frontend_request().state; + if (state == State::NONE) { + + if (!_w.client_request_acceptable()) { + throw Insufficient_buffer(); + } + using Op = Cbe::Request::Operation; + + bool const accepted = + _w.submit_frontend_request(*this, const_cast(src), + count, Op::WRITE, _snap_id); + if (!accepted) { return WRITE_ERR_IO; } + } + + _w.handle_frontend_request(); + state = _w.frontend_request().state; + + if ( state == State::PENDING + || state == State::IN_PROGRESS) { + _w.enqueue_handle(*this); + throw Insufficient_buffer(); + } + + if (state == State::COMPLETE) { + out_count = _w.frontend_request().count; + _w.ack_frontend_request(*this); + return WRITE_OK; + } + + if (state == State::ERROR_EOF) { + out_count = 0; + _w.ack_frontend_request(*this); + return WRITE_OK; + } + + if (state == State::ERROR) { + out_count = 0; + _w.ack_frontend_request(*this); + return WRITE_ERR_IO; + } + + return WRITE_ERR_IO; + } + + Sync_result sync() override + { + Genode::Mutex::Guard guard { _w.frontend_mtx() }; + + using State = Wrapper::Frontend_request::State; + + State state = _w.frontend_request().state; + if (state == State::NONE) { + + if (!_w.client_request_acceptable()) { + return SYNC_QUEUED; + } + using Op = Cbe::Request::Operation; + + bool const accepted = + _w.submit_frontend_request(*this, nullptr, 0, Op::SYNC, 0); + if (!accepted) { return SYNC_ERR_INVALID; } + } + + _w.handle_frontend_request(); + state = _w.frontend_request().state; + + if ( state == State::PENDING + || state == State::IN_PROGRESS) { + _w.enqueue_handle(*this); + return SYNC_QUEUED; + } + + if (state == State::COMPLETE) { + _w.ack_frontend_request(*this); + return SYNC_OK; + } + + if (state == State::ERROR) { + _w.ack_frontend_request(*this); + return SYNC_ERR_INVALID; + } + + return SYNC_ERR_INVALID; + } + + bool read_ready() override { return true; } + }; + + Data_file_system(Wrapper &w, uint32_t snap_id) + : + Single_file_system(Node_type::CONTINUOUS_FILE, type_name(), + Node_rwx::rw(), Xml_node("")), + _w(w), _snap_id(snap_id) + { } + + ~Data_file_system() { /* XXX sync on close */ } + + + /********************************* + ** Directory-service interface ** + *********************************/ + + Stat_result stat(char const *path, Stat &out) override + { + try { + (void)_w.cbe(); + } catch (...) { + return STAT_ERR_NO_ENTRY; + } + + Stat_result result = Single_file_system::stat(path, out); + + /* max_vba range is from 0 ... N - 1 */ + out.size = (_w.cbe().max_vba() + 1) * Cbe::BLOCK_SIZE; + return result; + } + + + /******************************** + ** File I/O service interface ** + ********************************/ + + Ftruncate_result ftruncate(Vfs::Vfs_handle *handle, file_size) override + { + return FTRUNCATE_OK; + } + + + /*************************** + ** File-system interface ** + ***************************/ + + Open_result open(char const *path, unsigned, + Vfs::Vfs_handle **out_handle, + Allocator &alloc) override + { + if (!_single_file(path)) + return OPEN_ERR_UNACCESSIBLE; + + try { + (void)_w.cbe(); + } catch (...) { + return OPEN_ERR_UNACCESSIBLE; + } + + *out_handle = + new (alloc) Vfs_handle(*this, *this, alloc, _w, _snap_id); + + return OPEN_OK; + } + + static char const *type_name() { return "data"; } + char const *type() override { return type_name(); } +}; + + +class Vfs_cbe::Extend_file_system : public Vfs::Single_file_system +{ + private: + + Wrapper &_w; + + struct Vfs_handle : Single_vfs_handle + { + Wrapper &_w; + + Vfs_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Wrapper &w) + : + Single_vfs_handle(ds, fs, alloc, 0), + _w(w) + { } + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + if (seek() != 0) { + out_count = 0; + return READ_OK; + } + + using Extending = Wrapper::Extending; + + Extending const exp = _w.extending_progress(); + + bool const in_progress = + exp.state == Extending::State::IN_PROGRESS; + bool const last_result = + !in_progress && exp.last_result != Extending::Result::NONE; + bool const success = + exp.last_result == Extending::Result::SUCCESS; + + using Result = Genode::String<32>; + Result result { + Extending::state_to_cstring(exp.state), + " last-result:", last_result ? success ? + "success" : "failed" : "none", + "\n" }; + copy_cstring(dst, result.string(), count); + size_t const length_without_nul = result.length() - 1; + out_count = count > length_without_nul - 1 ? + length_without_nul : count; + return READ_OK; + } + + Write_result write(char const *src, file_size count, file_size &out_count) override + { + using Type = Wrapper::Extending::Type; + using State = Wrapper::Extending::State; + if (_w.extending_progress().state != State::IDLE) { + return WRITE_ERR_IO; + } + + char tree[16]; + Arg_string::find_arg(src, "tree").string(tree, sizeof (tree), "-"); + Type type = Wrapper::Extending::string_to_type(tree); + if (type == Type::INVALID) { + return WRITE_ERR_IO; + } + + unsigned long blocks = Arg_string::find_arg(src, "blocks").ulong_value(0); + if (blocks == 0) { + return WRITE_ERR_IO; + } + + bool const okay = _w.start_extending(type, blocks); + if (!okay) { + return WRITE_ERR_IO; + } + + out_count = count; + return WRITE_OK; + } + + bool read_ready() override { return true; } + }; + + public: + + Extend_file_system(Wrapper &w) + : + Single_file_system(Node_type::TRANSACTIONAL_FILE, type_name(), + Node_rwx::wo(), Xml_node("")), + _w(w) + { } + + static char const *type_name() { return "extend"; } + + char const *type() override { return type_name(); } + + + /********************************* + ** Directory-service interface ** + *********************************/ + + Open_result open(char const *path, unsigned, + Vfs::Vfs_handle **out_handle, + Genode::Allocator &alloc) override + { + if (!_single_file(path)) + return OPEN_ERR_UNACCESSIBLE; + + try { + *out_handle = + new (alloc) Vfs_handle(*this, *this, alloc, _w); + return OPEN_OK; + } + catch (Genode::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; } + catch (Genode::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; } + } + + Stat_result stat(char const *path, Stat &out) override + { + Stat_result result = Single_file_system::stat(path, out); + return result; + } + + + /******************************** + ** File I/O service interface ** + ********************************/ + + Ftruncate_result ftruncate(Vfs::Vfs_handle *handle, file_size) override + { + return FTRUNCATE_OK; + } +}; + + +class Vfs_cbe::Rekey_file_system : public Vfs::Single_file_system +{ + private: + + Wrapper &_w; + + struct Vfs_handle : Single_vfs_handle + { + Wrapper &_w; + + Vfs_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Wrapper &w) + : + Single_vfs_handle(ds, fs, alloc, 0), + _w(w) + { } + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + if (seek() != 0) { + out_count = 0; + return READ_OK; + } + + Wrapper::Rekeying const & rkp = + _w.rekeying_progress(); + + using Result = Genode::String<32>; + using Rekeying = Wrapper::Rekeying; + bool const in_progress = + rkp.state == Rekeying::State::IN_PROGRESS; + bool const last_result = + !in_progress && rkp.last_result != Rekeying::Result::NONE; + bool const success = + rkp.last_result == Rekeying::Result::SUCCESS; + + Result result { + Rekeying::state_to_cstring(rkp.state), + " last-result:", last_result ? success ? + "success" : "failed" : "none", + "\n" }; + copy_cstring(dst, result.string(), count); + size_t const length_without_nul = result.length() - 1; + out_count = count > length_without_nul - 1 ? + length_without_nul : count; + return READ_OK; + } + + Write_result write(char const *src, file_size count, file_size &out_count) override + { + using State = Wrapper::Rekeying::State; + if (_w.rekeying_progress().state != State::IDLE) { + return WRITE_ERR_IO; + } + + bool start_rekeying { false }; + Genode::ascii_to(src, start_rekeying); + + if (!start_rekeying) { + return WRITE_ERR_IO; + } + + if (!_w.start_rekeying()) { + return WRITE_ERR_IO; + } + + out_count = count; + return WRITE_OK; + } + + bool read_ready() override { return true; } + }; + + public: + + Rekey_file_system(Wrapper &w) + : + Single_file_system(Node_type::TRANSACTIONAL_FILE, type_name(), + Node_rwx::wo(), Xml_node("")), + _w(w) + { } + + static char const *type_name() { return "rekey"; } + + char const *type() override { return type_name(); } + + + /********************************* + ** Directory-service interface ** + *********************************/ + + Open_result open(char const *path, unsigned, + Vfs::Vfs_handle **out_handle, + Genode::Allocator &alloc) override + { + if (!_single_file(path)) + return OPEN_ERR_UNACCESSIBLE; + + try { + *out_handle = + new (alloc) Vfs_handle(*this, *this, alloc, _w); + return OPEN_OK; + } + catch (Genode::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; } + catch (Genode::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; } + } + + Stat_result stat(char const *path, Stat &out) override + { + Stat_result result = Single_file_system::stat(path, out); + return result; + } + + + /******************************** + ** File I/O service interface ** + ********************************/ + + Ftruncate_result ftruncate(Vfs::Vfs_handle *handle, file_size) override + { + return FTRUNCATE_OK; + } +}; + + +class Vfs_cbe::Create_snapshot_file_system : public Vfs::Single_file_system +{ + private: + + Wrapper &_w; + + struct Vfs_handle : Single_vfs_handle + { + Wrapper &_w; + + Vfs_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Wrapper &w) + : + Single_vfs_handle(ds, fs, alloc, 0), + _w(w) + { } + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + return READ_ERR_IO; + } + + Write_result write(char const *src, file_size count, file_size &out_count) override + { + bool create_snapshot { false }; + Genode::ascii_to(src, create_snapshot); + Genode::String<64> str(Genode::Cstring(src, count)); + + if (!create_snapshot) { + return WRITE_ERR_IO; + } + + if (!_w.create_snapshot()) { + out_count = 0; + return WRITE_OK; + } + + out_count = count; + return WRITE_OK; + } + + bool read_ready() override { return true; } + }; + + public: + + Create_snapshot_file_system(Wrapper &w) + : + Single_file_system(Node_type::TRANSACTIONAL_FILE, type_name(), + Node_rwx::wo(), Xml_node("")), + _w(w) + { } + + static char const *type_name() { return "create_snapshot"; } + + char const *type() override { return type_name(); } + + + /********************************* + ** Directory-service interface ** + *********************************/ + + Open_result open(char const *path, unsigned, + Vfs::Vfs_handle **out_handle, + Genode::Allocator &alloc) override + { + if (!_single_file(path)) + return OPEN_ERR_UNACCESSIBLE; + + try { + *out_handle = + new (alloc) Vfs_handle(*this, *this, alloc, _w); + return OPEN_OK; + } + catch (Genode::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; } + catch (Genode::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; } + } + + Stat_result stat(char const *path, Stat &out) override + { + Stat_result result = Single_file_system::stat(path, out); + return result; + } + + + /******************************** + ** File I/O service interface ** + ********************************/ + + Ftruncate_result ftruncate(Vfs::Vfs_handle *handle, file_size) override + { + return FTRUNCATE_OK; + } +}; + + +class Vfs_cbe::Discard_snapshot_file_system : public Vfs::Single_file_system +{ + private: + + Wrapper &_w; + + struct Vfs_handle : Single_vfs_handle + { + Wrapper &_w; + + Vfs_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Wrapper &w) + : + Single_vfs_handle(ds, fs, alloc, 0), + _w(w) + { } + + Read_result read(char *, file_size, file_size &) override + { + return READ_ERR_IO; + } + + Write_result write(char const *src, file_size count, + file_size &out_count) override + { + out_count = 0; + + Genode::uint64_t id { 0 }; + Genode::ascii_to(src, id); + if (id == 0) { + return WRITE_ERR_IO; + } + + if (!_w.discard_snapshot(Cbe::Generation { id })) { + out_count = 0; + return WRITE_OK; + } + + return WRITE_ERR_IO; + } + + bool read_ready() override { return true; } + }; + + public: + + Discard_snapshot_file_system(Wrapper &w) + : + Single_file_system(Node_type::TRANSACTIONAL_FILE, type_name(), + Node_rwx::wo(), Xml_node("")), + _w(w) + { } + + static char const *type_name() { return "discard_snapshot"; } + + char const *type() override { return type_name(); } + + + /********************************* + ** Directory-service interface ** + *********************************/ + + Open_result open(char const *path, unsigned, + Vfs::Vfs_handle **out_handle, + Genode::Allocator &alloc) override + { + if (!_single_file(path)) + return OPEN_ERR_UNACCESSIBLE; + + try { + *out_handle = + new (alloc) Vfs_handle(*this, *this, alloc, _w); + return OPEN_OK; + } + catch (Genode::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; } + catch (Genode::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; } + } + + Stat_result stat(char const *path, Stat &out) override + { + Stat_result result = Single_file_system::stat(path, out); + return result; + } + + /******************************** + ** File I/O service interface ** + ********************************/ + + Ftruncate_result ftruncate(Vfs::Vfs_handle *handle, file_size) override + { + return FTRUNCATE_OK; + } +}; + + +struct Vfs_cbe::Snapshot_local_factory : File_system_factory +{ + Data_file_system _block_fs; + + Snapshot_local_factory(Vfs::Env &env, + Wrapper &cbe, + uint32_t snap_id) + : _block_fs(cbe, snap_id) { } + + Vfs::File_system *create(Vfs::Env&, Xml_node node) override + { + if (node.has_type(Data_file_system::type_name())) + return &_block_fs; + + return nullptr; + } +}; + + +class Vfs_cbe::Snapshot_file_system : private Snapshot_local_factory, + public Vfs::Dir_file_system +{ + private: + + Genode::uint32_t _snap_id; + + typedef String<128> Config; + + static Config _config(Genode::uint32_t snap_id, bool readonly) + { + char buf[Config::capacity()] { }; + + Xml_generator xml(buf, sizeof(buf), "dir", [&] () { + + xml.attribute("name", + !readonly ? String<16>("current") + : String<16>(snap_id)); + + xml.node("data", [&] () { + xml.attribute("readonly", readonly); + }); + }); + + return Config(Cstring(buf)); + } + + public: + + Snapshot_file_system(Vfs::Env &vfs_env, + Wrapper &cbe, + Genode::uint32_t snap_id, + bool readonly = false) + : + Snapshot_local_factory(vfs_env, cbe, snap_id), + Vfs::Dir_file_system(vfs_env, + Xml_node(_config(snap_id, readonly).string()), + *this), + _snap_id(snap_id) + { } + + static char const *type_name() { return "snapshot"; } + + char const *type() override { return type_name(); } + + Genode::uint32_t snapshot_id() const + { + return _snap_id; + } +}; + + +class Vfs_cbe::Snapshots_file_system : public Vfs::File_system +{ + private: + + Vfs::Env &_vfs_env; + + bool _root_dir(char const *path) { return strcmp(path, "/snapshots") == 0; } + bool _top_dir(char const *path) { return strcmp(path, "/") == 0; } + + struct Snapshot_registry + { + Genode::Allocator &_alloc; + Wrapper &_w; + + struct Invalid_index : Genode::Exception { }; + struct Invalid_path : Genode::Exception { }; + + uint32_t _number_of_snapshots { 0 }; + + Genode::Registry> _snap_fs { }; + + Snapshot_registry(Genode::Allocator &alloc, Wrapper &w) + : _alloc(alloc), _w(w) + { } + + void update(Vfs::Env &vfs_env) + { + Cbe::Active_snapshot_ids list { }; + _w.active_snapshot_ids(list); + + /* alloc new */ + for (size_t i = 0; i < sizeof (list.values) / sizeof (list.values[0]); i++) { + uint32_t const id = list.values[i]; + if (!id) { continue; } + + bool is_old = false; + auto find_old = [&] (Snapshot_file_system const &fs) { + is_old |= (fs.snapshot_id() == id); + }; + _snap_fs.for_each(find_old); + + if (!is_old) { + new (_alloc) Genode::Registered( + _snap_fs, vfs_env, _w, id, true); + ++_number_of_snapshots; + } + } + + /* destroy old */ + auto find_stale = [&] (Snapshot_file_system const &fs) { + bool is_stale = true; + for (size_t i = 0; i < sizeof (list.values) / sizeof (list.values[0]); i++) { + uint32_t const id = list.values[i]; + if (!id) { continue; } + + if (fs.snapshot_id() == id) { + is_stale = false; + break; + } + } + + if (is_stale) { + destroy(&_alloc, &const_cast(fs)); + --_number_of_snapshots; + } + }; + _snap_fs.for_each(find_stale); + } + + uint32_t number_of_snapshots() const { return _number_of_snapshots; } + + Snapshot_file_system const &by_index(uint32_t idx) const + { + uint32_t i = 0; + Snapshot_file_system const *fsp { nullptr }; + auto lookup = [&] (Snapshot_file_system const &fs) { + if (i == idx) { + fsp = &fs; + } + ++i; + }; + _snap_fs.for_each(lookup); + if (fsp == nullptr) { + throw Invalid_index(); + } + return *fsp; + } + + Snapshot_file_system &_by_id(uint32_t id) + { + Snapshot_file_system *fsp { nullptr }; + auto lookup = [&] (Snapshot_file_system &fs) { + if (fs.snapshot_id() == id) { + fsp = &fs; + } + }; + _snap_fs.for_each(lookup); + if (fsp == nullptr) { + throw Invalid_path(); + } + return *fsp; + } + + Snapshot_file_system &by_path(char const *path) + { + if (!path) { + throw Invalid_path(); + } + + if (path[0] == '/') { + path++; + } + + uint32_t id { 0 }; + Genode::ascii_to(path, id); + return _by_id(id); + } + }; + + public: + + struct Snap_vfs_handle : Vfs::Vfs_handle + { + using Vfs_handle::Vfs_handle; + + virtual Read_result read(char *dst, file_size count, + file_size &out_count) = 0; + + virtual Write_result write(char const *src, file_size count, + file_size &out_count) = 0; + + virtual Sync_result sync() + { + return SYNC_OK; + } + + virtual bool read_ready() = 0; + }; + + + struct Dir_vfs_handle : Snap_vfs_handle + { + Snapshot_registry const &_snap_reg; + + bool const _root_dir { false }; + + Read_result _query_snapshots(file_size index, + file_size &out_count, + Dirent &out) + { + if (index >= _snap_reg.number_of_snapshots()) { + out_count = sizeof(Dirent); + out.type = Dirent_type::END; + return READ_OK; + } + + try { + Snapshot_file_system const &fs = _snap_reg.by_index(index); + Genode::String<32> name { fs.snapshot_id() }; + + out = { + .fileno = (Genode::addr_t)this | index, + .type = Dirent_type::DIRECTORY, + .rwx = Node_rwx::rx(), + .name = { name.string() }, + }; + out_count = sizeof(Dirent); + return READ_OK; + } catch (Snapshot_registry::Invalid_index) { + return READ_ERR_INVALID; + } + } + + Read_result _query_root(file_size index, + file_size &out_count, + Dirent &out) + { + if (index == 0) { + out = { + .fileno = (Genode::addr_t)this, + .type = Dirent_type::DIRECTORY, + .rwx = Node_rwx::rx(), + .name = { "snapshots" } + }; + } else { + out.type = Dirent_type::END; + } + + out_count = sizeof(Dirent); + return READ_OK; + } + + Dir_vfs_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Snapshot_registry const &snap_reg, + bool root_dir) + : + Snap_vfs_handle(ds, fs, alloc, 0), + _snap_reg(snap_reg), _root_dir(root_dir) + { } + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + out_count = 0; + + if (count < sizeof(Dirent)) + return READ_ERR_INVALID; + + file_size index = seek() / sizeof(Dirent); + + Dirent &out = *(Dirent*)dst; + + if (!_root_dir) { + + /* opended as "/snapshots" */ + return _query_snapshots(index, out_count, out); + + } else { + /* opened as "/" */ + return _query_root(index, out_count, out); + } + } + + Write_result write(char const *, file_size, file_size &) override + { + return WRITE_ERR_INVALID; + } + + bool read_ready() override { return true; } + + }; + + struct Dir_snap_vfs_handle : Vfs::Vfs_handle + { + Vfs_handle &vfs_handle; + + Dir_snap_vfs_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Vfs::Vfs_handle &vfs_handle) + : Vfs_handle(ds, fs, alloc, 0), vfs_handle(vfs_handle) { } + + ~Dir_snap_vfs_handle() + { + vfs_handle.close(); + } + }; + + Snapshot_registry _snap_reg; + + char const *_sub_path(char const *path) const + { + /* skip heading slash in path if present */ + if (path[0] == '/') { + path++; + } + + Genode::size_t const name_len = strlen(type_name()); + if (strcmp(path, type_name(), name_len) != 0) { + return nullptr; + } + + path += name_len; + + /* + * The first characters of the first path element are equal to + * the current directory name. Let's check if the length of the + * first path element matches the name length. + */ + if (*path != 0 && *path != '/') { + return 0; + } + + return path; + } + + + Snapshots_file_system(Vfs::Env &vfs_env, + Genode::Xml_node node, + Wrapper &w) + : _vfs_env(vfs_env), _snap_reg(vfs_env.alloc(), w) + { } + + static char const *type_name() { return "snapshots"; } + + char const *type() override { return type_name(); } + + + /********************************* + ** Directory service interface ** + *********************************/ + + Dataspace_capability dataspace(char const *path) + { + return Genode::Dataspace_capability(); + } + + void release(char const *path, Dataspace_capability) + { + } + + Open_result open(char const *path, + unsigned mode, + Vfs::Vfs_handle **out_handle, + Allocator &alloc) override + { + path = _sub_path(path); + if (!path || path[0] != '/') { + return OPEN_ERR_UNACCESSIBLE; + } + + try { + Snapshot_file_system &fs = _snap_reg.by_path(path); + return fs.open(path, mode, out_handle, alloc); + } catch (Snapshot_registry::Invalid_path) { } + + return OPEN_ERR_UNACCESSIBLE; + } + + Opendir_result opendir(char const *path, + bool create, + Vfs::Vfs_handle **out_handle, + Allocator &alloc) override + { + if (create) { + return OPENDIR_ERR_PERMISSION_DENIED; + } + + bool const top = _top_dir(path); + if (_root_dir(path) || top) { + _snap_reg.update(_vfs_env); + + *out_handle = new (alloc) Dir_vfs_handle(*this, *this, alloc, + _snap_reg, top); + return OPENDIR_OK; + } else { + char const *sub_path = _sub_path(path); + if (!sub_path) { + return OPENDIR_ERR_LOOKUP_FAILED; + } + try { + Snapshot_file_system &fs = _snap_reg.by_path(sub_path); + Vfs::Vfs_handle *handle = nullptr; + Opendir_result const res = fs.opendir(sub_path, create, &handle, alloc); + if (res != OPENDIR_OK) { + return OPENDIR_ERR_LOOKUP_FAILED; + } + *out_handle = new (alloc) Dir_snap_vfs_handle(*this, *this, + alloc, *handle); + return OPENDIR_OK; + } catch (Snapshot_registry::Invalid_path) { } + } + return OPENDIR_ERR_LOOKUP_FAILED; + } + + void close(Vfs_handle *handle) override + { + if (handle && (&handle->ds() == this)) + destroy(handle->alloc(), handle); + } + + Stat_result stat(char const *path, Stat &out_stat) override + { + out_stat = Stat { }; + path = _sub_path(path); + + /* path does not match directory name */ + if (!path) { + return STAT_ERR_NO_ENTRY; + } + + /* + * If path equals directory name, return information about the + * current directory. + */ + if (strlen(path) == 0 || _top_dir(path)) { + + out_stat.type = Node_type::DIRECTORY; + out_stat.inode = 1; + out_stat.device = (Genode::addr_t)this; + return STAT_OK; + } + + if (!path || path[0] != '/') { + return STAT_ERR_NO_ENTRY; + } + + try { + Snapshot_file_system &fs = _snap_reg.by_path(path); + Stat_result const res = fs.stat(path, out_stat); + return res; + } catch (Snapshot_registry::Invalid_path) { } + + return STAT_ERR_NO_ENTRY; + } + + Unlink_result unlink(char const *path) + { + return UNLINK_ERR_NO_PERM; + } + + Rename_result rename(char const *from, char const *to) override + { + return RENAME_ERR_NO_PERM; + } + + file_size num_dirent(char const *path) override + { + if (_top_dir(path) || _root_dir(path)) { + _snap_reg.update(_vfs_env); + file_size const num = _snap_reg.number_of_snapshots(); + return num; + } + _snap_reg.update(_vfs_env); + + path = _sub_path(path); + if (!path) { + return 0; + } + try { + Snapshot_file_system &fs = _snap_reg.by_path(path); + file_size const num = fs.num_dirent(path); + return num; + } catch (Snapshot_registry::Invalid_path) { + return 0; + } + } + + bool directory(char const *path) override + { + if (_root_dir(path)) { + return true; + } + + path = _sub_path(path); + if (!path) { + return false; + } + try { + Snapshot_file_system &fs = _snap_reg.by_path(path); + return fs.directory(path); + } catch (Snapshot_registry::Invalid_path) { } + + return false; + } + + char const *leaf_path(char const *path) override + { + path = _sub_path(path); + if (!path) { + return nullptr; + } + + if (strlen(path) == 0 || strcmp(path, "") == 0) { + return path; + } + + try { + Snapshot_file_system &fs = _snap_reg.by_path(path); + char const *leaf_path = fs.leaf_path(path); + if (leaf_path) { + return leaf_path; + } + } catch (Snapshot_registry::Invalid_path) { } + + return nullptr; + } + + + /******************************** + ** File I/O service interface ** + ********************************/ + + Write_result write(Vfs::Vfs_handle *vfs_handle, + char const *buf, file_size buf_size, + file_size &out_count) override + { + return WRITE_ERR_IO; + } + + bool queue_read(Vfs::Vfs_handle *vfs_handle, file_size size) override + { + Dir_snap_vfs_handle *dh = + dynamic_cast(vfs_handle); + if (dh) { + return dh->vfs_handle.fs().queue_read(&dh->vfs_handle, + size); + } + + return true; + } + + Read_result complete_read(Vfs::Vfs_handle *vfs_handle, + char *dst, file_size count, + file_size & out_count) override + { + Snap_vfs_handle *sh = + dynamic_cast(vfs_handle); + if (sh) { + Read_result const res = sh->read(dst, count, out_count); + return res; + } + + Dir_snap_vfs_handle *dh = + dynamic_cast(vfs_handle); + if (dh) { + return dh->vfs_handle.fs().complete_read(&dh->vfs_handle, + dst, count, out_count); + } + + return READ_ERR_IO; + } + + bool read_ready(Vfs::Vfs_handle *) override + { + return true; + } + + Ftruncate_result ftruncate(Vfs::Vfs_handle *vfs_handle, + file_size len) override + { + return FTRUNCATE_OK; + } +}; + + +struct Vfs_cbe::Control_local_factory : File_system_factory +{ + Rekey_file_system _rekeying_fs; + Create_snapshot_file_system _create_snapshot_fs; + Discard_snapshot_file_system _discard_snapshot_fs; + Extend_file_system _extend_fs; + + Control_local_factory(Vfs::Env &env, + Xml_node config, + Wrapper &cbe) + : + _rekeying_fs(cbe), + _create_snapshot_fs(cbe), + _discard_snapshot_fs(cbe), + _extend_fs(cbe) + { } + + Vfs::File_system *create(Vfs::Env&, Xml_node node) override + { + if (node.has_type(Rekey_file_system::type_name())) { + return &_rekeying_fs; + } + + if (node.has_type(Create_snapshot_file_system::type_name())) { + return &_create_snapshot_fs; + } + + if (node.has_type(Discard_snapshot_file_system::type_name())) { + return &_discard_snapshot_fs; + } + + if (node.has_type(Extend_file_system::type_name())) { + return &_extend_fs; + } + + return nullptr; + } +}; + + +class Vfs_cbe::Control_file_system : private Control_local_factory, + public Vfs::Dir_file_system +{ + private: + + typedef String<128> Config; + + static Config _config(Xml_node node) + { + char buf[Config::capacity()] { }; + + Xml_generator xml(buf, sizeof(buf), "dir", [&] () { + xml.attribute("name", "control"); + xml.node("rekey", [&] () { }); + xml.node("extend", [&] () { }); + xml.node("create_snapshot", [&] () { }); + xml.node("discard_snapshot", [&] () { }); + }); + + return Config(Cstring(buf)); + } + + public: + + Control_file_system(Vfs::Env &vfs_env, + Genode::Xml_node node, + Wrapper &cbe) + : + Control_local_factory(vfs_env, node, cbe), + Vfs::Dir_file_system(vfs_env, Xml_node(_config(node).string()), + *this) + { } + + static char const *type_name() { return "control"; } + + char const *type() override { return type_name(); } +}; + + +struct Vfs_cbe::Local_factory : File_system_factory +{ + Snapshot_file_system _current_snapshot_fs; + Snapshots_file_system _snapshots_fs; + Control_file_system _control_fs; + + Local_factory(Vfs::Env &env, Xml_node config, + Wrapper &cbe) + : + _current_snapshot_fs(env, cbe, 0, false), + _snapshots_fs(env, config, cbe), + _control_fs(env, config, cbe) + { } + + Vfs::File_system *create(Vfs::Env&, Xml_node node) override + { + using Name = String<64>; + if (node.has_type(Snapshot_file_system::type_name()) + && node.attribute_value("name", Name()) == "current") + return &_current_snapshot_fs; + + if (node.has_type(Control_file_system::type_name())) + return &_control_fs; + + if (node.has_type(Snapshots_file_system::type_name())) + return &_snapshots_fs; + + return nullptr; + } +}; + + +class Vfs_cbe::File_system : private Local_factory, + public Vfs::Dir_file_system +{ + private: + + Wrapper &_wrapper; + + typedef String<256> Config; + + static Config _config(Xml_node node) + { + char buf[Config::capacity()] { }; + + Xml_generator xml(buf, sizeof(buf), "dir", [&] () { + typedef String<64> Name; + + xml.attribute("name", + node.attribute_value("name", + Name("cbe"))); + + xml.node("control", [&] () { }); + + xml.node("snapshot", [&] () { + xml.attribute("name", "current"); + }); + + xml.node("snapshots", [&] () { }); + }); + + return Config(Cstring(buf)); + } + + public: + + File_system(Vfs::Env &vfs_env, Genode::Xml_node node, + Wrapper &wrapper) + : + Local_factory(vfs_env, node, wrapper), + Vfs::Dir_file_system(vfs_env, Xml_node(_config(node).string()), + *this), + _wrapper(wrapper) + { } + + ~File_system() + { + // XXX rather then destroying the wrapper here, it should be + // done on the out-side where it was allocated in the first + // place but the factory interface does not support that yet + // destroy(vfs_env.alloc().alloc()), &_wrapper); + } +}; + + +/************************** + ** VFS plugin interface ** + **************************/ + +extern "C" Vfs::File_system_factory *vfs_file_system_factory(void) +{ + struct Factory : Vfs::File_system_factory + { + Vfs::File_system *create(Vfs::Env &vfs_env, + Genode::Xml_node node) override + { + try { + /* XXX wrapper is not managed and will leak */ + Vfs_cbe::Wrapper *wrapper = + new (vfs_env.alloc()) Vfs_cbe::Wrapper { vfs_env, node }; + return new (vfs_env.alloc()) + Vfs_cbe::File_system(vfs_env, node, *wrapper); + } catch (...) { + Genode::error("could not create 'cbe_fs' "); + } + return nullptr; + } + }; + + /* the CBE library requires a stack larger than the default */ + Genode::Thread::myself()->stack_size(64*1024); + + Cbe::assert_valid_object_size(); + + cbe_cxx_init(); + + static Factory factory; + return &factory; +} + + +/* + * The SPARK compiler might generate a call to memcmp when it wants to + * compare objects. For the time being we implement here and hopefully + * any other memcmp symbol has at least the same semantics. + */ +extern "C" int memcmp(const void *s1, const void *s2, Genode::size_t n) +{ + return Genode::memcmp(s1, s2, n); +} diff --git a/repos/gems/src/lib/vfs/cbe_crypto/aes_cbc/aes_cbc.cc b/repos/gems/src/lib/vfs/cbe_crypto/aes_cbc/aes_cbc.cc new file mode 100644 index 0000000000..02203db2ac --- /dev/null +++ b/repos/gems/src/lib/vfs/cbe_crypto/aes_cbc/aes_cbc.cc @@ -0,0 +1,284 @@ +/* + * \brief Integration of the Consistent Block Encrypter (CBE) + * \author Martin Stein + * \author Josef Soentgen + * \date 2020-11-10 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include +#include + +#include + +#include +#include + +namespace { + +using namespace Genode; + +struct Crypto : Cbe_crypto::Interface +{ + struct Buffer_size_mismatch : Genode::Exception { }; + struct Key_value_size_mismatch : Genode::Exception { }; + + struct { + uint32_t id { }; + Aes_cbc_4k::Key key { }; + bool used { false }; + } keys [Slots::NUM_SLOTS]; + + struct { + struct crypt_ring { + unsigned head { 0 }; + unsigned tail { 0 }; + + struct { + Cbe::Request request { }; + Cbe::Block_data data { }; + } queue [4]; + + unsigned max() const { + return sizeof(queue) / sizeof(queue[0]); } + + bool acceptable() const { + return ((head + 1) % max() != tail); } + + template + bool enqueue(FUNC const &fn) + { + if (!acceptable()) + return false; + + fn(queue[head]); + head = (head + 1) % max(); + + return true; + } + + template + bool apply_crypt(FUNC const &fn) + { + if (head == tail) + return false; + + if (!fn(queue[tail])) + return false; + + tail = (tail + 1) % max(); + return true; + } + }; + + struct crypt_ring encrypt; + struct crypt_ring decrypt; + + template + bool queue_encrypt(FUNC const &fn) { return encrypt.enqueue(fn); } + + template + bool apply_encrypt(FUNC const &fn) { return encrypt.apply_crypt(fn); } + + template + bool queue_decrypt(FUNC const &fn) { return decrypt.enqueue(fn); } + + template + bool apply_decrypt(FUNC const &fn) { return decrypt.apply_crypt(fn); } + } jobs { }; + + template + bool apply_to_unused_key(FUNC const &fn) + { + for (unsigned i = 0; i < sizeof(keys) / sizeof(keys[0]); i++) { + if (keys[i].used) continue; + + return fn(keys[i]); + } + return false; + } + + template + bool apply_key(uint32_t const id, FUNC const &fn) + { + for (unsigned i = 0; i < sizeof(keys) / sizeof(keys[0]); i++) { + if (!keys[i].used || id != keys[i].id) continue; + + return fn(keys[i]); + } + + return false; + } + + Crypto() { } + + /*************** + ** interface ** + ***************/ + + bool execute() override + { + return true; + } + + bool add_key(uint32_t const id, + char const * const value, + size_t value_len) override + { + return apply_to_unused_key([&](auto &key_slot) { + if (value_len != sizeof(key_slot.key)) + return false; + + if (!_slots.store(id)) + return false; + + Genode::memcpy(key_slot.key.values, value, sizeof(key_slot.key)); + key_slot.id = id; + key_slot.used = true; + + return true; + }); + } + + bool remove_key(uint32_t const id) override + { + return apply_key (id, [&] (auto &meta) { + Genode::memset(meta.key.values, 0, sizeof(meta.key.values)); + + meta.used = false; + + _slots.remove(id); + return true; + }); + } + + bool submit_encryption_request(uint64_t const block_number, + uint32_t const key_id, + char const *src, + size_t const src_len) override + { + if (!src || src_len != sizeof (Cbe::Block_data)) { + error("buffer has wrong size"); + throw Buffer_size_mismatch(); + } + + if (!jobs.encrypt.acceptable()) + return false; + + return apply_key (key_id, [&] (auto &meta) { + return jobs.queue_encrypt([&] (auto &job) { + job.request = Cbe::Request(Cbe::Request::Operation::WRITE, + false, block_number, 0, 1, key_id, 0); + + uint64_t block_id = job.request.block_number(); + + Aes_cbc_4k::Block_number block_number { block_id }; + Aes_cbc_4k::Plaintext const &plaintext = *reinterpret_cast(src); + Aes_cbc_4k::Ciphertext &ciphertext = *reinterpret_cast(&job.data); + + /* paranoia */ + static_assert(sizeof(plaintext) == sizeof(job.data), "size mismatch"); + + Aes_cbc_4k::encrypt(meta.key, block_number, plaintext, ciphertext); + }); + }); + } + + Complete_request encryption_request_complete(char * const dst, + size_t const dst_len) override + { + static_assert(sizeof(Cbe::Block_data) == sizeof(Aes_cbc_4k::Ciphertext), "size mismatch"); + static_assert(sizeof(Cbe::Block_data) == sizeof(Aes_cbc_4k::Plaintext), "size mismatch"); + + if (dst_len != sizeof (Cbe::Block_data)) { + error("buffer has wrong size"); + throw Buffer_size_mismatch(); + } + + uint64_t block_id = 0; + + bool const valid = jobs.apply_encrypt([&](auto const &job) { + Genode::memcpy(dst, &job.data, sizeof(job.data)); + + block_id = job.request.block_number(); + + return true; + }); + + return Complete_request { .valid = valid, + .block_number = block_id }; + } + + bool submit_decryption_request(uint64_t const block_number, + uint32_t const key_id, + char const *src, + size_t const src_len) override + { + if (src_len != sizeof (Cbe::Block_data)) { + error("buffer has wrong size"); + throw Buffer_size_mismatch(); + } + + if (!jobs.decrypt.acceptable()) + return false; + + /* use apply_key to make sure key_id is actually known */ + return apply_key (key_id, [&] (auto &) { + return jobs.queue_decrypt([&] (auto &job) { + job.request = Cbe::Request(Cbe::Request::Operation::READ, + false, block_number, 0, 1, key_id, 0); + Genode::memcpy(&job.data, src, sizeof(job.data)); + }); + }); + } + + Complete_request decryption_request_complete(char *dst, size_t dst_len) override + { + static_assert(sizeof(Cbe::Block_data) == sizeof(Aes_cbc_4k::Ciphertext), "size mismatch"); + static_assert(sizeof(Cbe::Block_data) == sizeof(Aes_cbc_4k::Plaintext), "size mismatch"); + + if (dst_len != sizeof (Cbe::Block_data)) { + error("buffer has wrong size"); + throw Buffer_size_mismatch(); + } + + uint64_t block_id = 0; + + bool const valid = jobs.apply_decrypt([&](auto const &job) { + bool ok = apply_key (job.request.key_id(), [&] (auto &meta) { + block_id = job.request.block_number(); + + Aes_cbc_4k::Block_number block_number { block_id }; + Aes_cbc_4k::Ciphertext const &ciphertext = *reinterpret_cast(&job.data); + Aes_cbc_4k::Plaintext &plaintext = *reinterpret_cast(dst); + + /* paranoia */ + static_assert(sizeof(ciphertext) == sizeof(job.data), "size mismatch"); + + Aes_cbc_4k::decrypt(meta.key, block_number, ciphertext, plaintext); + + return true; + }); + + return ok; + }); + + return Complete_request { .valid = valid, + .block_number = block_id }; + } +}; + +} /* anonymous namespace */ + + +Cbe_crypto::Interface &Cbe_crypto::get_interface() +{ + static Crypto inst; + return inst; +} diff --git a/repos/gems/src/lib/vfs/cbe_crypto/aes_cbc/dummy.ads b/repos/gems/src/lib/vfs/cbe_crypto/aes_cbc/dummy.ads new file mode 100644 index 0000000000..bf6121ad6e --- /dev/null +++ b/repos/gems/src/lib/vfs/cbe_crypto/aes_cbc/dummy.ads @@ -0,0 +1,17 @@ +-- +-- \brief Integration of the Consistent Block Encrypter (CBE) +-- \author Martin Stein +-- \author Josef Soentgen +-- \date 2020-11-10 +-- + +-- +-- Copyright (C) 2020 Genode Labs GmbH +-- +-- This file is part of the Genode OS framework, which is distributed +-- under the terms of the GNU Affero General Public License version 3. +-- + +package Dummy +is +end Dummy; diff --git a/repos/gems/src/lib/vfs/cbe_crypto/aes_cbc/target.mk b/repos/gems/src/lib/vfs/cbe_crypto/aes_cbc/target.mk new file mode 100644 index 0000000000..3ae9ca776d --- /dev/null +++ b/repos/gems/src/lib/vfs/cbe_crypto/aes_cbc/target.mk @@ -0,0 +1,3 @@ +TARGET := lib-vfs-cbe_crypto-aes_cbc +REQUIRES = x86_64 +LIBS = vfs_cbe_crypto_aes_cbc diff --git a/repos/gems/src/lib/vfs/cbe_crypto/memcopy/memcopy.cc b/repos/gems/src/lib/vfs/cbe_crypto/memcopy/memcopy.cc new file mode 100644 index 0000000000..ac18e01d3b --- /dev/null +++ b/repos/gems/src/lib/vfs/cbe_crypto/memcopy/memcopy.cc @@ -0,0 +1,141 @@ +/* + * \brief Integration of the Consistent Block Encrypter (CBE) + * \author Martin Stein + * \author Josef Soentgen + * \date 2020-11-10 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* Genode includes */ +#include +#include + +/* cbe_crypto includes */ +#include + +namespace { + +using namespace Genode; + +struct Crypto : Cbe_crypto::Interface +{ + char _internal_buffer[Cbe_crypto::BLOCK_SIZE] { }; + + struct Request + { + uint64_t block_number; + bool pending; + }; + + Request _request { 0, false }; + + Crypto() { } + + /*************** + ** interface ** + ***************/ + + bool execute() override + { + return false; + } + + bool add_key(uint32_t const id, char const *, size_t) override + { + if (!_slots.store(id)) { + return false; + } + + log("Add key: id " , id); + return true; + } + + bool remove_key(uint32_t const id) override + { + log("Remove key: id " , id); + _slots.remove(id); + return true; + } + + bool _submit_request(uint64_t const block_number, + uint32_t const /* key_id */, + char const *src, + size_t const src_len) + { + if (_request.pending) { + return false; + } + + if (src_len < sizeof (_internal_buffer)) { + error("buffer too small"); + throw Buffer_too_small(); + } + + _request.pending = true; + _request.block_number = block_number; + + Genode::memcpy(_internal_buffer, src, sizeof (_internal_buffer)); + return true; + } + + bool submit_encryption_request(uint64_t const block_number, + uint32_t const key_id, + char const *src, + size_t const src_len) override + { + return _submit_request(block_number, key_id, src, src_len); + } + + Complete_request _request_complete(char *dst, size_t const dst_len) + { + if (!_request.pending) { + return Complete_request { .valid = false, .block_number = 0 }; + } + + if (dst_len < sizeof (_internal_buffer)) { + error("buffer too small"); + throw Buffer_too_small(); + } + + Genode::memcpy(dst, _internal_buffer, sizeof (_internal_buffer)); + + _request.pending = false; + + return Complete_request { + .valid = true, + .block_number = _request.block_number }; + } + + Complete_request encryption_request_complete(char *dst, size_t const dst_len) override + { + return _request_complete(dst, dst_len); + } + + bool submit_decryption_request(uint64_t const block_number, + uint32_t const key_id, + char const *src, + size_t const src_len) override + { + return _submit_request(block_number, key_id, src, src_len); + } + + Complete_request decryption_request_complete(char *dst, size_t dst_len) override + { + return _request_complete(dst, dst_len); + } +}; + +} /* anonymous namespace */ + + +Cbe_crypto::Interface &Cbe_crypto::get_interface() +{ + static Crypto inst; + return inst; +} diff --git a/repos/gems/src/lib/vfs/cbe_crypto/memcopy/target.mk b/repos/gems/src/lib/vfs/cbe_crypto/memcopy/target.mk new file mode 100644 index 0000000000..2056779d98 --- /dev/null +++ b/repos/gems/src/lib/vfs/cbe_crypto/memcopy/target.mk @@ -0,0 +1,3 @@ +TARGET := lib-vfs-cbe_crypto-memcopy +REQUIRES = x86_64 +LIBS = vfs_cbe_crypto_memcopy diff --git a/repos/gems/src/lib/vfs/cbe_crypto/vfs.cc b/repos/gems/src/lib/vfs/cbe_crypto/vfs.cc new file mode 100644 index 0000000000..9251b1ddb9 --- /dev/null +++ b/repos/gems/src/lib/vfs/cbe_crypto/vfs.cc @@ -0,0 +1,1172 @@ +/* + * \brief Integration of the Consistent Block Encrypter (CBE) + * \author Martin Stein + * \author Josef Soentgen + * \date 2020-11-10 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* Genode includes */ +#include +#include +#include +#include + +/* CBE includes */ +#include + + +namespace Vfs_cbe_crypto { + + using namespace Vfs; + using namespace Genode; + + class Encrypt_file_system; + class Decrypt_file_system; + + class Key_local_factory; + class Key_file_system; + + class Keys_file_system; + + class Management_file_system; + struct Add_key_file_system; + struct Remove_key_file_system; + + struct Local_factory; + class File_system; +} + + +class Vfs_cbe_crypto::Encrypt_file_system : public Vfs::Single_file_system +{ + private: + + Cbe_crypto::Interface &_crypto; + uint32_t _key_id; + + struct Encrypt_handle : Single_vfs_handle + { + Cbe_crypto::Interface &_crypto; + uint32_t _key_id; + + enum State { NONE, PENDING }; + State _state; + + Encrypt_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Cbe_crypto::Interface &crypto, + uint32_t key_id) + : + Single_vfs_handle { ds, fs, alloc, 0 }, + _crypto { crypto }, + _key_id { key_id }, + _state { State::NONE } + { } + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + if (_state != State::PENDING) { + return READ_ERR_IO; + } + + _crypto.execute(); + + try { + Cbe_crypto::Interface::Complete_request const cr = + _crypto.encryption_request_complete(dst, count); + if (!cr.valid) { + return READ_ERR_INVALID; + } + + _state = State::NONE; + + out_count = count; + return READ_OK; + } catch (Cbe_crypto::Interface::Buffer_too_small) { + return READ_ERR_INVALID; + } + + return READ_ERR_IO; + } + + Write_result write(char const *src, file_size count, + file_size &out_count) override + { + if (_state != State::NONE) { + return WRITE_ERR_IO; + } + + try { + uint64_t const block_number = seek() / Cbe_crypto::BLOCK_SIZE; + bool const ok = + _crypto.submit_encryption_request(block_number, _key_id, src, count); + if (!ok) { + out_count = 0; + return WRITE_OK; + } + _state = State::PENDING; + } catch (Cbe_crypto::Interface::Buffer_too_small) { + return WRITE_ERR_INVALID; + } + + _crypto.execute(); + out_count = count; + return WRITE_OK; + } + + bool read_ready() override { return true; } + }; + + public: + + Encrypt_file_system(Cbe_crypto::Interface &crypto, uint32_t key_id) + : + Single_file_system { Node_type::TRANSACTIONAL_FILE, type_name(), + Node_rwx::rw(), Xml_node("") }, + _crypto { crypto }, + _key_id { key_id } + { } + + static char const *type_name() { return "encrypt"; } + + char const *type() override { return type_name(); } + + /********************************* + ** Directory-service interface ** + *********************************/ + + Open_result open(char const *path, unsigned, + Vfs::Vfs_handle **out_handle, + Genode::Allocator &alloc) override + { + if (!_single_file(path)) + return OPEN_ERR_UNACCESSIBLE; + + try { + *out_handle = + new (alloc) Encrypt_handle(*this, *this, alloc, + _crypto, _key_id); + return OPEN_OK; + } + catch (Genode::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; } + catch (Genode::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; } + } + + Stat_result stat(char const *path, Stat &out) override + { + Stat_result result = Single_file_system::stat(path, out); + return result; + } + + /******************************** + ** File I/O service interface ** + ********************************/ + + Ftruncate_result ftruncate(Vfs::Vfs_handle *, file_size) override + { + return FTRUNCATE_OK; + } +}; + + +class Vfs_cbe_crypto::Decrypt_file_system : public Vfs::Single_file_system +{ + private: + + Cbe_crypto::Interface &_crypto; + uint32_t _key_id; + + struct Decrypt_handle : Single_vfs_handle + { + Cbe_crypto::Interface &_crypto; + uint32_t _key_id; + + enum State { NONE, PENDING }; + State _state; + + Decrypt_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Cbe_crypto::Interface &crypto, + uint32_t key_id) + : + Single_vfs_handle { ds, fs, alloc, 0 }, + _crypto { crypto }, + _key_id { key_id }, + _state { State::NONE } + { } + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + if (_state != State::PENDING) { + return READ_ERR_IO; + } + + _crypto.execute(); + + try { + Cbe_crypto::Interface::Complete_request const cr = + _crypto.decryption_request_complete(dst, count); + if (cr.valid) { + } + _state = State::NONE; + + out_count = count; + return READ_OK; + } catch (Cbe_crypto::Interface::Buffer_too_small) { + return READ_ERR_INVALID; + } + + return READ_ERR_IO; + } + + Write_result write(char const *src, file_size count, + file_size &out_count) override + { + if (_state != State::NONE) { + return WRITE_ERR_IO; + } + + try { + uint64_t const block_number = seek() / Cbe_crypto::BLOCK_SIZE; + bool const ok = + _crypto.submit_decryption_request(block_number, _key_id, src, count); + if (!ok) { + out_count = 0; + return WRITE_OK; + } + _state = State::PENDING; + } catch (Cbe_crypto::Interface::Buffer_too_small) { + return WRITE_ERR_INVALID; + } + + _crypto.execute(); + out_count = count; + return WRITE_OK; + } + + bool read_ready() override { return true; } + }; + + public: + + Decrypt_file_system(Cbe_crypto::Interface &crypto, uint32_t key_id) + : + Single_file_system { Node_type::TRANSACTIONAL_FILE, type_name(), + Node_rwx::rw(), Xml_node("") }, + _crypto { crypto }, + _key_id { key_id } + { } + + static char const *type_name() { return "decrypt"; } + + char const *type() override { return type_name(); } + + /********************************* + ** Directory-service interface ** + *********************************/ + + Open_result open(char const *path, unsigned /* flags */, + Vfs::Vfs_handle **out_handle, + Genode::Allocator &alloc) override + { + if (!_single_file(path)) + return OPEN_ERR_UNACCESSIBLE; + + try { + *out_handle = + new (alloc) Decrypt_handle(*this, *this, alloc, + _crypto, _key_id); + return OPEN_OK; + } + catch (Genode::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; } + catch (Genode::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; } + } + + Stat_result stat(char const *path, Stat &out) override + { + Stat_result result = Single_file_system::stat(path, out); + return result; + } + + /******************************** + ** File I/O service interface ** + ********************************/ + + Ftruncate_result ftruncate(Vfs::Vfs_handle *, file_size) override + { + return FTRUNCATE_OK; + } +}; + + +struct Vfs_cbe_crypto::Key_local_factory : File_system_factory +{ + Encrypt_file_system _encrypt_fs; + Decrypt_file_system _decrypt_fs; + + Key_local_factory(Cbe_crypto::Interface &crypto, + uint32_t key_id) + : + _encrypt_fs { crypto, key_id }, + _decrypt_fs { crypto, key_id } + { } + + Vfs::File_system *create(Vfs::Env&, Xml_node node) override + { + if (node.has_type(Encrypt_file_system::type_name())) + return &_encrypt_fs; + + if (node.has_type(Decrypt_file_system::type_name())) + return &_decrypt_fs; + + return nullptr; + } +}; + + +class Vfs_cbe_crypto::Key_file_system : private Key_local_factory, + public Vfs::Dir_file_system +{ + private: + + uint32_t _key_id; + + typedef String<128> Config; + + static Config _config(uint32_t key_id) + { + char buf[Config::capacity()] { }; + + Xml_generator xml(buf, sizeof(buf), "dir", [&] () { + + xml.attribute("name", String<16>(key_id)); + + xml.node("decrypt", [&] () { }); + xml.node("encrypt", [&] () { }); + }); + + return Config(Cstring(buf)); + } + + public: + + Key_file_system(Vfs::Env &vfs_env, + Cbe_crypto::Interface &crypto, + uint32_t key_id) + : + Key_local_factory { crypto, key_id }, + Vfs::Dir_file_system { vfs_env, + Xml_node(_config(key_id).string()), + *this }, + _key_id { key_id } + { } + + static char const *type_name() { return "keys"; } + + char const *type() override { return type_name(); } + + uint32_t key_id() const + { + return _key_id; + } +}; + + +class Vfs_cbe_crypto::Keys_file_system : public Vfs::File_system +{ + private: + + Vfs::Env &_vfs_env; + + bool _root_dir(char const *path) { return strcmp(path, "/keys") == 0; } + bool _top_dir(char const *path) { return strcmp(path, "/") == 0; } + + struct Key_registry + { + Genode::Allocator &_alloc; + Cbe_crypto::Interface &_crypto; + + struct Invalid_index : Genode::Exception { }; + struct Invalid_path : Genode::Exception { }; + + uint32_t _number_of_keys { 0 }; + + Genode::Registry> _key_fs { }; + + Key_registry(Genode::Allocator &alloc, Cbe_crypto::Interface &crypto) + : + _alloc { alloc }, + _crypto { crypto } + { } + + void update(Vfs::Env &vfs_env) + { + _crypto.for_each_key([&] (uint32_t const id) { + + bool already_known = false; + auto lookup = [&] (Key_file_system &fs) { + already_known |= fs.key_id() == id; + }; + _key_fs.for_each(lookup); + + if (!already_known) { + new (_alloc) Genode::Registered( + _key_fs, vfs_env, _crypto, id); + ++_number_of_keys; + } + }); + + auto find_stale_keys = [&] (Key_file_system const &fs) { + bool active_key = false; + _crypto.for_each_key([&] (uint32_t const id) { + active_key |= id == fs.key_id(); + }); + + if (!active_key) { + destroy(&_alloc, &const_cast(fs)); + --_number_of_keys; + } + }; + _key_fs.for_each(find_stale_keys); + } + + uint32_t number_of_keys() const { return _number_of_keys; } + + Key_file_system const &by_index(uint32_t idx) const + { + uint32_t i = 0; + Key_file_system const *fsp { nullptr }; + auto lookup = [&] (Key_file_system const &fs) { + if (i == idx) { + fsp = &fs; + } + ++i; + }; + _key_fs.for_each(lookup); + if (fsp == nullptr) { + throw Invalid_index(); + } + return *fsp; + } + + Key_file_system &_by_id(uint32_t id) + { + Key_file_system *fsp { nullptr }; + auto lookup = [&] (Key_file_system &fs) { + if (fs.key_id() == id) { + fsp = &fs; + } + }; + _key_fs.for_each(lookup); + if (fsp == nullptr) { + throw Invalid_path(); + } + return *fsp; + } + + Key_file_system &by_path(char const *path) + { + if (!path) { + throw Invalid_path(); + } + + if (path[0] == '/') { + path++; + } + + uint32_t id { 0 }; + Genode::ascii_to(path, id); + return _by_id(id); + } + }; + + public: + + struct Local_vfs_handle : Vfs::Vfs_handle + { + using Vfs_handle::Vfs_handle; + + virtual Read_result read(char *dst, file_size count, + file_size &out_count) = 0; + + virtual Write_result write(char const *src, file_size count, + file_size &out_count) = 0; + + virtual Sync_result sync() + { + return SYNC_OK; + } + + virtual bool read_ready() = 0; + }; + + + struct Dir_vfs_handle : Local_vfs_handle + { + Key_registry const &_key_reg; + + bool const _root_dir { false }; + + Read_result _query_keys(file_size index, + file_size &out_count, + Dirent &out) + { + if (index >= _key_reg.number_of_keys()) { + out_count = sizeof(Dirent); + out.type = Dirent_type::END; + return READ_OK; + } + + try { + Key_file_system const &fs = _key_reg.by_index(index); + Genode::String<32> name { fs.key_id() }; + + out = { + .fileno = (Genode::addr_t)this | index, + .type = Dirent_type::DIRECTORY, + .rwx = Node_rwx::rx(), + .name = { name.string() }, + }; + out_count = sizeof(Dirent); + return READ_OK; + } catch (Key_registry::Invalid_index) { + return READ_ERR_INVALID; + } + } + + Read_result _query_root(file_size index, + file_size &out_count, + Dirent &out) + { + if (index == 0) { + out = { + .fileno = (Genode::addr_t)this, + .type = Dirent_type::DIRECTORY, + .rwx = Node_rwx::rx(), + .name = { "keys" } + }; + } else { + out.type = Dirent_type::END; + } + + out_count = sizeof(Dirent); + return READ_OK; + } + + Dir_vfs_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Key_registry const &key_reg, + bool root_dir) + : + Local_vfs_handle(ds, fs, alloc, 0), + _key_reg(key_reg), _root_dir(root_dir) + { } + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + out_count = 0; + + if (count < sizeof(Dirent)) + return READ_ERR_INVALID; + + file_size index = seek() / sizeof(Dirent); + + Dirent &out = *(Dirent*)dst; + + if (!_root_dir) { + + /* opended as "/" */ + return _query_keys(index, out_count, out); + + } else { + /* opened as "/" */ + return _query_root(index, out_count, out); + } + } + + Write_result write(char const *, file_size, file_size &) override + { + return WRITE_ERR_INVALID; + } + + bool read_ready() override { return true; } + + }; + + struct Dir_snap_vfs_handle : Vfs::Vfs_handle + { + Vfs_handle &vfs_handle; + + Dir_snap_vfs_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Vfs::Vfs_handle &vfs_handle) + : Vfs_handle(ds, fs, alloc, 0), vfs_handle(vfs_handle) { } + + ~Dir_snap_vfs_handle() + { + vfs_handle.close(); + } + }; + + Key_registry _key_reg; + + char const *_sub_path(char const *path) const + { + /* skip heading slash in path if present */ + if (path[0] == '/') { + path++; + } + + Genode::size_t const name_len = strlen(type_name()); + if (strcmp(path, type_name(), name_len) != 0) { + return nullptr; + } + + path += name_len; + + /* + * The first characters of the first path element are equal to + * the current directory name. Let's check if the length of the + * first path element matches the name length. + */ + if (*path != 0 && *path != '/') { + return 0; + } + + return path; + } + + + Keys_file_system(Vfs::Env &vfs_env, + Cbe_crypto::Interface &crypto) + : + _vfs_env { vfs_env }, + _key_reg { vfs_env.alloc(), crypto } + { } + + static char const *type_name() { return "keys"; } + + char const *type() override { return type_name(); } + + + /********************************* + ** Directory service interface ** + *********************************/ + + Dataspace_capability dataspace(char const *) override + { + return Genode::Dataspace_capability(); + } + + void release(char const *, Dataspace_capability) override { } + + Open_result open(char const *path, + unsigned mode, + Vfs::Vfs_handle **out_handle, + Allocator &alloc) override + { + _key_reg.update(_vfs_env); + + path = _sub_path(path); + if (!path || path[0] != '/') { + return OPEN_ERR_UNACCESSIBLE; + } + + try { + Key_file_system &fs = _key_reg.by_path(path); + return fs.open(path, mode, out_handle, alloc); + } catch (Key_registry::Invalid_path) { } + + return OPEN_ERR_UNACCESSIBLE; + } + + Opendir_result opendir(char const *path, + bool create, + Vfs::Vfs_handle **out_handle, + Allocator &alloc) override + { + if (create) { + return OPENDIR_ERR_PERMISSION_DENIED; + } + + _key_reg.update(_vfs_env); + + bool const top = _top_dir(path); + if (_root_dir(path) || top) { + + *out_handle = new (alloc) Dir_vfs_handle(*this, *this, alloc, + _key_reg, top); + return OPENDIR_OK; + } else { + char const *sub_path = _sub_path(path); + if (!sub_path) { + return OPENDIR_ERR_LOOKUP_FAILED; + } + try { + Key_file_system &fs = _key_reg.by_path(sub_path); + Vfs::Vfs_handle *handle = nullptr; + Opendir_result const res = fs.opendir(sub_path, create, &handle, alloc); + if (res != OPENDIR_OK) { + return OPENDIR_ERR_LOOKUP_FAILED; + } + *out_handle = new (alloc) Dir_snap_vfs_handle(*this, *this, + alloc, *handle); + return OPENDIR_OK; + } catch (Key_registry::Invalid_path) { } + } + return OPENDIR_ERR_LOOKUP_FAILED; + } + + void close(Vfs_handle *handle) override + { + if (handle && (&handle->ds() == this)) + destroy(handle->alloc(), handle); + } + + Stat_result stat(char const *path, Stat &out_stat) override + { + out_stat = Stat { }; + path = _sub_path(path); + + /* path does not match directory name */ + if (!path) { + return STAT_ERR_NO_ENTRY; + } + + /* + * If path equals directory name, return information about the + * current directory. + */ + if (strlen(path) == 0 || _top_dir(path)) { + + out_stat.type = Node_type::DIRECTORY; + out_stat.inode = 1; + out_stat.device = (Genode::addr_t)this; + return STAT_OK; + } + + if (!path || path[0] != '/') { + return STAT_ERR_NO_ENTRY; + } + + try { + Key_file_system &fs = _key_reg.by_path(path); + Stat_result const res = fs.stat(path, out_stat); + return res; + } catch (Key_registry::Invalid_path) { } + + return STAT_ERR_NO_ENTRY; + } + + Unlink_result unlink(char const *) override + { + return UNLINK_ERR_NO_PERM; + } + + Rename_result rename(char const *, char const *) override + { + return RENAME_ERR_NO_PERM; + } + + file_size num_dirent(char const *path) override + { + _key_reg.update(_vfs_env); + + if (_top_dir(path) || _root_dir(path)) { + file_size const num = _key_reg.number_of_keys(); + return num; + } + + path = _sub_path(path); + if (!path) { + return 0; + } + try { + Key_file_system &fs = _key_reg.by_path(path); + file_size const num = fs.num_dirent(path); + return num; + } catch (Key_registry::Invalid_path) { + return 0; + } + } + + bool directory(char const *path) override + { + if (_root_dir(path)) { + return true; + } + + path = _sub_path(path); + if (!path) { + return false; + } + try { + Key_file_system &fs = _key_reg.by_path(path); + return fs.directory(path); + } catch (Key_registry::Invalid_path) { } + + return false; + } + + char const *leaf_path(char const *path) override + { + path = _sub_path(path); + if (!path) { + return nullptr; + } + + if (strlen(path) == 0 || strcmp(path, "") == 0) { + return path; + } + + try { + Key_file_system &fs = _key_reg.by_path(path); + char const *leaf_path = fs.leaf_path(path); + if (leaf_path) { + return leaf_path; + } + } catch (Key_registry::Invalid_path) { } + + return nullptr; + } + + + /******************************** + ** File I/O service interface ** + ********************************/ + + Write_result write(Vfs::Vfs_handle *, char const *, + file_size, file_size &) override + { + return WRITE_ERR_IO; + } + + bool queue_read(Vfs::Vfs_handle *vfs_handle, file_size size) override + { + Dir_snap_vfs_handle *dh = + dynamic_cast(vfs_handle); + if (dh) { + return dh->vfs_handle.fs().queue_read(&dh->vfs_handle, + size); + } + + return true; + } + + Read_result complete_read(Vfs::Vfs_handle *vfs_handle, + char *dst, file_size count, + file_size & out_count) override + { + Local_vfs_handle *lh = + dynamic_cast(vfs_handle); + if (lh) { + Read_result const res = lh->read(dst, count, out_count); + return res; + } + + Dir_snap_vfs_handle *dh = + dynamic_cast(vfs_handle); + if (dh) { + return dh->vfs_handle.fs().complete_read(&dh->vfs_handle, + dst, count, out_count); + } + + return READ_ERR_IO; + } + + bool read_ready(Vfs::Vfs_handle *) override + { + return true; + } + + Ftruncate_result ftruncate(Vfs::Vfs_handle *, file_size ) override + { + return FTRUNCATE_OK; + } +}; + + + + +class Vfs_cbe_crypto::Management_file_system : public Vfs::Single_file_system +{ + public: + + enum Type { ADD_KEY, REMOVE_KEY }; + + static char const *type_string(Type type) + { + switch (type) { + case Type::ADD_KEY: return "add"; + case Type::REMOVE_KEY: return "remove"; + } + return nullptr; + } + + private: + + Management_file_system(Management_file_system const &) = delete; + Management_file_system &operator=(Management_file_system const&) = delete; + + Type _type; + Cbe_crypto::Interface &_crypto; + + struct Manage_handle : Single_vfs_handle + { + Type _type; + Cbe_crypto::Interface &_crypto; + + Manage_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Type type, + Cbe_crypto::Interface &crypto) + : + Single_vfs_handle { ds, fs, alloc, 0 }, + _type { type }, + _crypto { crypto } + { } + + Read_result read(char *, file_size, file_size &) override + { + return READ_ERR_IO; + } + + Write_result write(char const *src, file_size count, + file_size &out_count) override + { + out_count = 0; + + if (seek() != 0) { + return WRITE_ERR_IO; + } + + if (src == nullptr || count < sizeof (uint32_t)) { + return WRITE_ERR_INVALID; + } + + uint32_t id = *reinterpret_cast(src); + if (id == 0) { + return WRITE_ERR_INVALID; + } + + if (_type == Type::ADD_KEY) { + + if (count != sizeof (uint32_t) + 32 /* XXX Cbe::Key::value*/) { + return WRITE_ERR_INVALID; + } + + try { + char const * value = src + sizeof (uint32_t); + size_t const value_len = count - sizeof (uint32_t); + if (_crypto.add_key(id, value, value_len)) { + out_count = count; + return WRITE_OK; + } + } catch (...) { } + + } else + + if (_type == Type::REMOVE_KEY) { + + if (count != sizeof (uint32_t)) { + return WRITE_ERR_INVALID; + } + + if (_crypto.remove_key(id)) { + out_count = count; + return WRITE_OK; + } + } + + return WRITE_ERR_IO; + } + + bool read_ready() override { return true; } + }; + + char const *_type_name; + + public: + + Management_file_system(Cbe_crypto::Interface &crypto, Type type, char const *type_name) + : + Single_file_system { Node_type::TRANSACTIONAL_FILE, type_name, + Node_rwx::wo(), Xml_node("") }, + _type { type }, + _crypto { crypto }, + _type_name { type_name } + { } + + char const *type() override { return _type_name; } + + /********************************* + ** Directory-service interface ** + *********************************/ + + Open_result open(char const *path, + unsigned /* flags */, + Vfs::Vfs_handle **out_handle, + Genode::Allocator &alloc) override + { + if (!_single_file(path)) { + return OPEN_ERR_UNACCESSIBLE; + } + + try { + *out_handle = + new (alloc) Manage_handle(*this, *this, alloc, + _type, _crypto); + return OPEN_OK; + } + catch (Genode::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; } + catch (Genode::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; } + } + + Stat_result stat(char const *path, Stat &out) override + { + Stat_result result = Single_file_system::stat(path, out); + return result; + } + + /******************************** + ** File I/O service interface ** + ********************************/ + + Ftruncate_result ftruncate(Vfs::Vfs_handle *, file_size) override + { + return FTRUNCATE_OK; + } +}; + + +struct Vfs_cbe_crypto::Add_key_file_system : public Vfs_cbe_crypto::Management_file_system +{ + static char const *type_name() { return "add_key"; } + + Add_key_file_system(Cbe_crypto::Interface &crypto) + : Management_file_system(crypto, Management_file_system::ADD_KEY, type_name()) { } + + char const *type() override { return type_name(); } +}; + + +struct Vfs_cbe_crypto::Remove_key_file_system : public Vfs_cbe_crypto::Management_file_system +{ + static char const *type_name() { return "remove_key"; } + + Remove_key_file_system(Cbe_crypto::Interface &crypto) + : Management_file_system(crypto, Management_file_system::REMOVE_KEY, type_name()) { } + + char const *type() override { return type_name(); } +}; + + +struct Vfs_cbe_crypto::Local_factory : File_system_factory +{ + Keys_file_system _keys_fs; + Add_key_file_system _add_key_fs; + Remove_key_file_system _remove_key_fs; + + Local_factory(Vfs::Env &env, + Cbe_crypto::Interface &crypto) + : + _keys_fs { env, crypto }, + _add_key_fs { crypto }, + _remove_key_fs { crypto } + { } + + Vfs::File_system *create(Vfs::Env&, Xml_node node) override + { + if (node.has_type(Add_key_file_system::type_name())) { + return &_add_key_fs; + } + + if (node.has_type(Remove_key_file_system::type_name())) { + return &_remove_key_fs; + } + + if (node.has_type(Keys_file_system::type_name())) { + return &_keys_fs; + } + + return nullptr; + } +}; + + +class Vfs_cbe_crypto::File_system : private Local_factory, + public Vfs::Dir_file_system +{ + private: + + typedef String<128> Config; + + static Config _config(Xml_node node) + { + (void)node; + char buf[Config::capacity()] { }; + + Xml_generator xml(buf, sizeof (buf), "dir", [&] () { + xml.attribute( + "name", node.attribute_value("name", String<64>(""))); + + xml.node("add_key", [&] () { }); + xml.node("remove_key", [&] () { }); + xml.node("keys", [&] () { }); + }); + + return Config(Cstring(buf)); + } + + public: + + File_system(Vfs::Env &vfs_env, Genode::Xml_node node) + : + Local_factory { vfs_env, Cbe_crypto::get_interface() }, + Vfs::Dir_file_system { vfs_env, Xml_node(_config(node).string()), + *this } + { } + + ~File_system() { } +}; + + +/************************** + ** VFS plugin interface ** + **************************/ + +extern "C" Vfs::File_system_factory *vfs_file_system_factory(void) +{ + struct Factory : Vfs::File_system_factory + { + Vfs::File_system *create(Vfs::Env &vfs_env, + Genode::Xml_node node) override + { + try { + return new (vfs_env.alloc()) + Vfs_cbe_crypto::File_system(vfs_env, node); + } catch (...) { + Genode::error("could not create 'cbe_crypto_aes_cbc'"); + } + return nullptr; + } + }; + + static Factory factory; + return &factory; +} diff --git a/repos/gems/src/lib/vfs/cbe_trust_anchor/target.mk b/repos/gems/src/lib/vfs/cbe_trust_anchor/target.mk new file mode 100644 index 0000000000..e63a473b2c --- /dev/null +++ b/repos/gems/src/lib/vfs/cbe_trust_anchor/target.mk @@ -0,0 +1,3 @@ +TARGET := lib-vfs-cbe_trust_anchor +REQUIRES = x86_64 +LIBS = vfs_cbe_trust_anchor diff --git a/repos/gems/src/lib/vfs/cbe_trust_anchor/vfs.cc b/repos/gems/src/lib/vfs/cbe_trust_anchor/vfs.cc new file mode 100644 index 0000000000..5981db9c15 --- /dev/null +++ b/repos/gems/src/lib/vfs/cbe_trust_anchor/vfs.cc @@ -0,0 +1,1751 @@ +/* + * \brief Integration of the Consistent Block Encrypter (CBE) + * \author Martin Stein + * \author Josef Soentgen + * \date 2020-11-10 + */ + +/* + * Copyright (C) 2020 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* Genode includes */ +#include +#include +#include +#include + +/* CBE includes */ +#include + + +static void xor_bytes(unsigned char const *p, int p_len, + unsigned char *k, int k_len) +{ + for (int i = 0; i < k_len; i++) { + k[i] ^= p[i % p_len]; + } +} + + +static void fill_bytes(unsigned char *v, int v_len) +{ + static unsigned char _fill_counter = 0x23; + + for (int i = 0; i < v_len; i++) { + v[i] = _fill_counter; + } + + ++_fill_counter; +} + + +namespace Vfs_cbe_trust_anchor { + + using namespace Vfs; + using namespace Genode; + + class Generate_key_file_system; + class Hashsum_file_system; + class Encrypt_file_system; + class Decrypt_file_system; + class Initialize_file_system; + + struct Local_factory; + class File_system; +} + + +class Trust_anchor +{ + public: + + using Path = Genode::Path<256>; + + Path const key_file_name { "keyfile" }; + Path const hash_file_name { "secured_superblock" }; + + struct Complete_request + { + bool valid; + bool success; + }; + + private: + + Trust_anchor(Trust_anchor const &) = delete; + Trust_anchor &operator=(Trust_anchor const&) = delete; + + using size_t = Genode::size_t; + + Vfs::Env &_vfs_env; + + enum class State { + UNINITIALIZED, + INITIALIZIE_IN_PROGRESS, + INITIALIZED, + }; + State _state { State::UNINITIALIZED }; + + enum class Lock_state { LOCKED, UNLOCKED }; + Lock_state _lock_state { Lock_state::LOCKED }; + + enum class Job { + NONE, + DECRYPT, + ENCRYPT, + GENERATE, + INIT, + READ_HASH, + UPDATE_HASH, + UNLOCK + }; + Job _job { Job::NONE }; + + enum class Job_state { NONE, PENDING, IN_PROGRESS, COMPLETE }; + Job_state _job_state { Job_state::NONE }; + + bool _job_success { false }; + + struct Private_key + { + enum { KEY_LEN = 32 }; + unsigned char value[KEY_LEN] { }; + }; + Private_key _private_key { }; + + struct Last_hash + { + enum { HASH_LEN = 32 }; + unsigned char value[HASH_LEN] { }; + static constexpr size_t length = HASH_LEN; + }; + Last_hash _last_hash { }; + + struct Key + { + enum { KEY_LEN = 32 }; + unsigned char value[KEY_LEN] { }; + static constexpr size_t length = KEY_LEN; + }; + Key _decrypt_key { }; + Key _encrypt_key { }; + Key _generated_key { }; + + void _xcrypt_key(Private_key const &priv_key, Key &key) + { + xor_bytes(priv_key.value, (int)Private_key::KEY_LEN, + key.value, (int)Key::KEY_LEN); + } + + void _fill_key(Key &key) + { + fill_bytes(key.value, (int)Key::KEY_LEN); + } + + bool _execute_xcrypt(Key &key) + { + switch (_job_state) { + case Job_state::PENDING: + _xcrypt_key(_private_key, key); + _job_state = Job_state::COMPLETE; + _job_success = true; + [[fallthrough]]; + case Job_state::COMPLETE: + return true; + + case Job_state::IN_PROGRESS: [[fallthrough]]; + case Job_state::NONE: [[fallthrough]]; + default: return false; + } + + /* never reached */ + return false; + } + + bool _execute_generate(Key &key) + { + switch (_job_state) { + case Job_state::PENDING: + _fill_key(key); + _job_state = Job_state::COMPLETE; + _job_success = true; + [[fallthrough]]; + case Job_state::COMPLETE: + return true; + + case Job_state::IN_PROGRESS: [[fallthrough]]; + case Job_state::NONE: [[fallthrough]]; + default: return false; + } + + /* never reached */ + return false; + } + + bool _execute_unlock() + { + bool progress = false; + + switch (_job_state) { + case Job_state::PENDING: + { + if (!_open_key_file_and_queue_read(_base_path)) { + break; + } + + _job_state = Job_state::IN_PROGRESS; + progress |= true; + } + + [[fallthrough]]; + case Job_state::IN_PROGRESS: + { + if (!_read_key_file_finished()) { + break; + } + + Private_key key { }; + + /* copy passphrase to key object */ + size_t const key_len = + Genode::min(_key_io_job_buffer.size, + sizeof (key.value)); + + Genode::memset(key.value, 0xa5, sizeof (key.value)); + Genode::memcpy(key.value, _key_io_job_buffer.buffer, key_len); + + _job_state = Job_state::COMPLETE; + _job_success = Genode::memcmp(_private_key.value, key.value, + sizeof (key.value)); + + progress |= true; + } + + [[fallthrough]]; + case Job_state::COMPLETE: + break; + + case Job_state::NONE: [[fallthrough]]; + default: break; + } + + return progress; + } + + bool _execute_init() + { + bool progress = false; + + switch (_job_state) { + case Job_state::PENDING: + { + if (!_open_key_file_and_write(_base_path)) { + _job_state = Job_state::COMPLETE; + _job_success = false; + return true; + } + + /* copy passphrase to key object */ + size_t const key_len = + Genode::min(_key_io_job_buffer.size, + sizeof (_private_key.value)); + Genode::memset(_private_key.value, 0xa5, sizeof (_private_key.value)); + Genode::memcpy(_private_key.value, _key_io_job_buffer.buffer, key_len); + + _job_state = Job_state::IN_PROGRESS; + progress |= true; + } + + [[fallthrough]]; + case Job_state::IN_PROGRESS: + if (!_write_key_file_finished()) { + break; + } + + _job_state = Job_state::COMPLETE; + _job_success = true; + + progress |= true; + [[fallthrough]]; + case Job_state::COMPLETE: + break; + + case Job_state::NONE: [[fallthrough]]; + default: break; + } + + return progress; + } + + bool _execute_read_hash() + { + bool progress = false; + + switch (_job_state) { + case Job_state::PENDING: + if (!_open_hash_file_and_queue_read(_base_path)) { + _job_state = Job_state::COMPLETE; + _job_success = false; + return true; + } + + _job_state = Job_state::IN_PROGRESS; + progress |= true; + + [[fallthrough]]; + case Job_state::IN_PROGRESS: + { + if (!_read_hash_file_finished()) { + break; + } + + size_t const hash_len = + Genode::min(_hash_io_job_buffer.size, + sizeof (_last_hash.value)); + Genode::memcpy(_last_hash.value, _hash_io_job_buffer.buffer, + hash_len); + + _job_state = Job_state::COMPLETE; + _job_success = true; + + progress |= true; + } + [[fallthrough]]; + case Job_state::COMPLETE: + break; + + case Job_state::NONE: [[fallthrough]]; + default: break; + } + + return progress; + } + + + bool _execute_update_hash() + { + bool progress = false; + + switch (_job_state) { + case Job_state::PENDING: + { + if (!_open_hash_file_and_write(_base_path)) { + _job_state = Job_state::COMPLETE; + _job_success = false; + return true; + } + + /* keep new hash in last hash */ + size_t const hash_len = + Genode::min(_hash_io_job_buffer.size, + sizeof (_hash_io_job_buffer.size)); + Genode::memcpy(_last_hash.value, _hash_io_job_buffer.buffer, + hash_len); + + _job_state = Job_state::IN_PROGRESS; + progress |= true; + } + [[fallthrough]]; + case Job_state::IN_PROGRESS: + if (!_write_hash_file_finished()) { + break; + } + + _job_state = Job_state::COMPLETE; + _job_success = true; + + progress |= true; + [[fallthrough]]; + case Job_state::COMPLETE: + break; + + case Job_state::NONE: [[fallthrough]]; + default: break; + } + + return progress; + } + + bool _execute() + { + switch (_job) { + case Job::DECRYPT: return _execute_xcrypt(_decrypt_key); + case Job::ENCRYPT: return _execute_xcrypt(_encrypt_key); + case Job::GENERATE: return _execute_generate(_generated_key); + case Job::INIT: return _execute_init(); + case Job::READ_HASH: return _execute_read_hash(); + case Job::UPDATE_HASH: return _execute_update_hash(); + case Job::UNLOCK : return _execute_unlock(); + case Job::NONE: [[fallthrough]]; + default: return false; + } + + /* never reached */ + return false; + } + + friend struct Io_response_handler; + + struct Io_response_handler : Vfs::Io_response_handler + { + Genode::Signal_context_capability _io_sigh; + + Io_response_handler(Genode::Signal_context_capability io_sigh) + : _io_sigh(io_sigh) { } + + void read_ready_response() override { } + + void io_progress_response() override + { + if (_io_sigh.valid()) { + Genode::Signal_transmitter(_io_sigh).submit(); + } + } + }; + + void _handle_io() + { + (void)_execute(); + } + + Genode::Io_signal_handler _io_handler { + _vfs_env.env().ep(), *this, &Trust_anchor::_handle_io }; + + Io_response_handler _io_response_handler { _io_handler }; + + /* key */ + + Vfs::Vfs_handle *_key_handle { nullptr }; + Genode::Constructible _key_io_job { }; + + struct Key_io_job_buffer : Util::Io_job::Buffer + { + char buffer[64] { }; + + Key_io_job_buffer() + { + Buffer::base = buffer; + Buffer::size = sizeof (buffer); + } + }; + + Key_io_job_buffer _key_io_job_buffer { }; + + bool _check_key_file(Path const &path) + { + Path file_path = path; + file_path.append_element(key_file_name.string()); + + using Stat_result = Vfs::Directory_service::Stat_result; + + Vfs::Directory_service::Stat out_stat { }; + Stat_result const stat_res = + _vfs_env.root_dir().stat(file_path.string(), out_stat); + + if (stat_res == Stat_result::STAT_OK) { + + _state = State::INITIALIZED; + return true; + } + _state = State::UNINITIALIZED; + return false; + } + + bool _open_key_file_and_queue_read(Path const &path) + { + Path file_path = path; + file_path.append_element(key_file_name.string()); + + using Result = Vfs::Directory_service::Open_result; + + Result const res = + _vfs_env.root_dir().open(file_path.string(), + Vfs::Directory_service::OPEN_MODE_RDONLY, + (Vfs::Vfs_handle **)&_key_handle, + _vfs_env.alloc()); + if (res != Result::OPEN_OK) { + Genode::error("could not open '", file_path.string(), "'"); + return false; + } + + _key_handle->handler(&_io_response_handler); + _key_io_job.construct(*_key_handle, Util::Io_job::Operation::READ, + _key_io_job_buffer, 0, + Util::Io_job::Partial_result::ALLOW); + if (_key_io_job->execute() && _key_io_job->completed()) { + _state = State::INITIALIZED; + _vfs_env.root_dir().close(_key_handle); + _key_handle = nullptr; + return true; + } + return true; + } + + bool _read_key_file_finished() + { + if (!_key_io_job.constructed()) { + return true; + } + + // XXX trigger sync + + bool const progress = _key_io_job->execute(); + bool const completed = _key_io_job->completed(); + if (completed) { + _state = State::INITIALIZED; + _vfs_env.root_dir().close(_key_handle); + _key_handle = nullptr; + _key_io_job.destruct(); + } + + return progress && completed; + } + + bool _open_key_file_and_write(Path const &path) + { + using Result = Vfs::Directory_service::Open_result; + + Path file_path = path; + file_path.append_element(key_file_name.string()); + + unsigned const mode = + Vfs::Directory_service::OPEN_MODE_WRONLY | Vfs::Directory_service::OPEN_MODE_CREATE; + + Result const res = + _vfs_env.root_dir().open(file_path.string(), mode, + (Vfs::Vfs_handle **)&_key_handle, + _vfs_env.alloc()); + if (res != Result::OPEN_OK) { + return false; + } + + _key_handle->handler(&_io_response_handler); + _key_io_job.construct(*_key_handle, Util::Io_job::Operation::WRITE, + _key_io_job_buffer, 0); + if (_key_io_job->execute() && _key_io_job->completed()) { + _state = State::INITIALIZED; + _vfs_env.root_dir().close(_key_handle); + _key_handle = nullptr; + _key_io_job.destruct(); + return true; + } + return true; + } + + bool _write_key_file_finished() + { + if (!_key_io_job.constructed()) { + return true; + } + + // XXX trigger sync + + bool const progress = _key_io_job->execute(); + bool const completed = _key_io_job->completed(); + if (completed) { + _state = State::INITIALIZED; + _vfs_env.root_dir().close(_key_handle); + _key_handle = nullptr; + _key_io_job.destruct(); + } + + return progress && completed; + } + + + /* hash */ + + Vfs::Vfs_handle *_hash_handle { nullptr }; + + Genode::Constructible _hash_io_job { }; + + struct Hash_io_job_buffer : Util::Io_job::Buffer + { + char buffer[64] { }; + + Hash_io_job_buffer() + { + Buffer::base = buffer; + Buffer::size = sizeof (buffer); + } + }; + + Hash_io_job_buffer _hash_io_job_buffer { }; + + bool _open_hash_file_and_queue_read(Path const &path) + { + using Result = Vfs::Directory_service::Open_result; + + Path file_path = path; + file_path.append_element(hash_file_name.string()); + + Result const res = + _vfs_env.root_dir().open(file_path.string(), + Vfs::Directory_service::OPEN_MODE_RDONLY, + (Vfs::Vfs_handle **)&_hash_handle, + _vfs_env.alloc()); + if (res != Result::OPEN_OK) { + return false; + } + + _hash_handle->handler(&_io_response_handler); + _hash_io_job.construct(*_hash_handle, Util::Io_job::Operation::READ, + _hash_io_job_buffer, 0, + Util::Io_job::Partial_result::ALLOW); + if (_hash_io_job->execute() && _hash_io_job->completed()) { + _vfs_env.root_dir().close(_hash_handle); + _hash_handle = nullptr; + _hash_io_job.destruct(); + return true; + } + return true; + } + + bool _read_hash_file_finished() + { + if (!_hash_io_job.constructed()) { + return true; + } + + // XXX trigger sync + + bool const progress = _hash_io_job->execute(); + bool const completed = _hash_io_job->completed(); + if (completed) { + _vfs_env.root_dir().close(_hash_handle); + _hash_handle = nullptr; + _hash_io_job.destruct(); + } + + return progress && completed; + } + + void _close_hash_handle() + { + _vfs_env.root_dir().close(_hash_handle); + Genode::destroy(_hash_handle->alloc(), _hash_handle); + _hash_handle = nullptr; + _hash_io_job.destruct(); + } + + bool _open_hash_file_and_write(Path const &path) + { + using Result = Vfs::Directory_service::Open_result; + + Path file_path = path; + file_path.append_element(hash_file_name.string()); + + using Stat_result = Vfs::Directory_service::Stat_result; + + Vfs::Directory_service::Stat out_stat { }; + Stat_result const stat_res = + _vfs_env.root_dir().stat(file_path.string(), out_stat); + + bool const file_exists = stat_res == Stat_result::STAT_OK; + + unsigned const mode = + Vfs::Directory_service::OPEN_MODE_WRONLY | + (file_exists ? 0 : Vfs::Directory_service::OPEN_MODE_CREATE); + + Result const res = + _vfs_env.root_dir().open(file_path.string(), mode, + (Vfs::Vfs_handle **)&_hash_handle, + _vfs_env.alloc()); + if (res != Result::OPEN_OK) { + Genode::error("could not open '", file_path.string(), "'"); + return false; + } + + _hash_handle->handler(&_io_response_handler); + _hash_io_job.construct(*_hash_handle, Util::Io_job::Operation::WRITE, + _hash_io_job_buffer, 0); + + if (_hash_io_job->execute() && _hash_io_job->completed()) { + _close_hash_handle(); + } + return true; + } + + bool _write_hash_file_finished() + { + if (!_hash_io_job.constructed()) { + return true; + } + + // XXX trigger sync + + bool const progress = _hash_io_job->execute(); + bool const completed = _hash_io_job->completed(); + if (completed) { + _close_hash_handle(); + } + + return progress && completed; + } + + Path const _base_path; + + public: + + Trust_anchor(Vfs::Env &vfs_env, Path const &path) + : + _vfs_env { vfs_env }, + _base_path { path } + { + if (_check_key_file(_base_path)) { + + if (_open_key_file_and_queue_read(_base_path)) { + + while (!_read_key_file_finished()) { + _vfs_env.env().ep().wait_and_dispatch_one_io_signal(); + } + } + } + else { + Genode::log("No key file found, TA not initialized"); + } + } + + bool initialized() const + { + return _state == State:: INITIALIZED; + } + + bool execute() + { + return _execute(); + } + + bool queue_initialize(char const *src, size_t len) + { + if (_job != Job::NONE) { + return false; + } + + if (_state != State::UNINITIALIZED) { + return false; + } + + if (len > _key_io_job_buffer.size) { + len = _key_io_job_buffer.size; + } + + _key_io_job_buffer.size = len; + + Genode::memcpy(_key_io_job_buffer.buffer, src, + _key_io_job_buffer.size); + + _job = Job::INIT; + _job_state = Job_state::PENDING; + return true; + } + + Complete_request complete_queue_initialize() + { + if (_job != Job::INIT || _job_state != Job_state::COMPLETE) { + return { false, false }; + } + + _lock_state = Lock_state::UNLOCKED; + + _job = Job::NONE; + _job_state = Job_state::NONE; + return { true, _job_success }; + } + + bool queue_unlock(char const *src, size_t len) + { + if (_job != Job::NONE) { + return false; + } + + if (_state != State::INITIALIZED) { + return false; + } + + if (_lock_state == Lock_state::UNLOCKED) { + _job = Job::UNLOCK; + _job_state = Job_state::COMPLETE; + _job_success = true; + return true; + } + + if (len > _key_io_job_buffer.size) { + len = _key_io_job_buffer.size; + } + + _key_io_job_buffer.size = len; + + Genode::memcpy(_key_io_job_buffer.buffer, src, + _key_io_job_buffer.size); + + _job = Job::UNLOCK; + _job_state = Job_state::PENDING; + return true; + } + + Complete_request complete_queue_unlock() + { + if (_job != Job::UNLOCK || _job_state != Job_state::COMPLETE) { + return { false, false }; + } + + _lock_state = Lock_state::UNLOCKED; + + _job = Job::NONE; + _job_state = Job_state::NONE; + return { true, _job_success }; + } + + bool queue_read_last_hash() + { + if (_job != Job::NONE) { + return false; + } + + if (_lock_state != Lock_state::UNLOCKED) { + return false; + } + + _job = Job::READ_HASH; + _job_state = Job_state::PENDING; + return true; + } + + Complete_request complete_read_last_hash(char *dst, size_t len) + { + if (_job != Job::READ_HASH || _job_state != Job_state::COMPLETE) { + return { false, false }; + } + + if (len < _last_hash.length) { + Genode::warning("truncate hash"); + } + + Genode::memcpy(dst, _last_hash.value, len); + + _job = Job::NONE; + _job_state = Job_state::NONE; + return { true, _job_success }; + } + + bool queue_update_last_hash(char const *src, size_t len) + { + if (_job != Job::NONE) { + return false; + } + + if (_lock_state != Lock_state::UNLOCKED) { + return false; + } + + if (len != _last_hash.length) { + return false; + } + + if (len > _hash_io_job_buffer.size) { + len = _hash_io_job_buffer.size; + } + + _hash_io_job_buffer.size = len; + + Genode::memcpy(_hash_io_job_buffer.buffer, src, + _hash_io_job_buffer.size); + + Genode::memcpy(_last_hash.value, src, len); + + _job = Job::UPDATE_HASH; + _job_state = Job_state::PENDING; + return true; + } + + Complete_request complete_update_last_hash() + { + if (_job != Job::UPDATE_HASH || _job_state != Job_state::COMPLETE) { + return { false, false }; + } + + _job = Job::NONE; + _job_state = Job_state::NONE; + return { true, _job_success }; + } + + bool queue_encrypt_key(char const *src, size_t len) + { + if (_job != Job::NONE) { + return false; + } + + if (_lock_state != Lock_state::UNLOCKED) { + return false; + } + + if (len != _encrypt_key.length) { + Genode::error(__func__, ": key length mismatch, expected: ", + _encrypt_key.length, " got: ", len); + return false; + } + + Genode::memcpy(_encrypt_key.value, src, len); + + _job = Job::ENCRYPT; + _job_state = Job_state::PENDING; + return true; + } + + Complete_request complete_encrypt_key(char *dst, size_t len) + { + if (_job != Job::ENCRYPT || _job_state != Job_state::COMPLETE) { + return { false, false }; + } + + if (len != _encrypt_key.length) { + Genode::error(__func__, ": key length mismatch, expected: ", + _encrypt_key.length, " got: ", len); + return { true, false }; + } + + Genode::memcpy(dst, _encrypt_key.value, _encrypt_key.length); + + _job = Job::NONE; + _job_state = Job_state::NONE; + return { true, _job_success }; + } + + bool queue_decrypt_key(char const *src, size_t len) + { + if (_job != Job::NONE) { + return false; + } + + if (_lock_state != Lock_state::UNLOCKED) { + return false; + } + + if (len != _decrypt_key.length) { + Genode::error(__func__, ": key length mismatch, expected: ", + _decrypt_key.length, " got: ", len); + return false; + } + + Genode::memcpy(_decrypt_key.value, src, len); + + _job = Job::DECRYPT; + _job_state = Job_state::PENDING; + return true; + } + + Complete_request complete_decrypt_key(char *dst, size_t len) + { + if (_job != Job::DECRYPT || _job_state != Job_state::COMPLETE) { + return { false, false }; + } + + if (len != _decrypt_key.length) { + Genode::error(__func__, ": key length mismatch, expected: ", + _decrypt_key.length, " got: ", len); + return { true, false }; + } + + Genode::memcpy(dst, _decrypt_key.value, _decrypt_key.length); + + _job = Job::NONE; + _job_state = Job_state::NONE; + return { true, _job_success }; + } + + bool queue_generate_key() + { + if (_job_state != Job_state::NONE) { + return false; + } + + _job = Job::GENERATE; + _job_state = Job_state::PENDING; + return true; + } + + Complete_request complete_generate_key(char *dst, size_t len) + { + if (_job != Job::GENERATE || _job_state != Job_state::COMPLETE) { + return { false, false }; + } + + if (len < _generated_key.length) { + Genode::warning("truncate generated key"); + } else + + if (len > _generated_key.length) { + len = _generated_key.length; + } + + Genode::memcpy(dst, _generated_key.value, len); + Genode::memset(_generated_key.value, 0, sizeof (_generated_key.value)); + + _job = Job::NONE; + _job_state = Job_state::NONE; + return { true, _job_success }; + } +}; + + +class Vfs_cbe_trust_anchor::Hashsum_file_system : public Vfs::Single_file_system +{ + private: + + Trust_anchor &_trust_anchor; + + struct Hashsum_handle : Single_vfs_handle + { + Trust_anchor &_trust_anchor; + + enum class State { NONE, PENDING_WRITE_ACK, PENDING_READ }; + State _state { State::NONE }; + + Hashsum_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Trust_anchor &ta) + : + Single_vfs_handle { ds, fs, alloc, 0 }, + _trust_anchor { ta } + { } + + Read_result read(char *src, file_size count, + file_size &out_count) override + { + if (_state == State::NONE) { + try { + bool const ok = + _trust_anchor.queue_read_last_hash(); + if (!ok) { + return READ_ERR_IO; + } + _state = State::PENDING_READ; + } catch (...) { + return READ_ERR_INVALID; + } + + _trust_anchor.execute(); + return READ_QUEUED; + } else + + if (_state == State::PENDING_READ) { + try { + Trust_anchor::Complete_request const cr = + _trust_anchor.complete_read_last_hash(src, count); + if (!cr.valid) { + _trust_anchor.execute(); + return READ_QUEUED; + } + + _state = State::NONE; + out_count = count; + return cr.success ? READ_OK : READ_ERR_IO; + } catch (...) { + return READ_ERR_INVALID; + } + } else + + if (_state == State::PENDING_WRITE_ACK) { + try { + Trust_anchor::Complete_request const cr = + _trust_anchor.complete_update_last_hash(); + if (!cr.valid) { + _trust_anchor.execute(); + return READ_QUEUED; + } + + _state = State::NONE; + out_count = count; + return cr.success ? READ_OK : READ_ERR_IO; + } catch (...) { + return READ_ERR_INVALID; + } + } + + return READ_ERR_IO; + } + + Write_result write(char const *src, file_size count, + file_size &out_count) override + { + if (_state != State::NONE) { + return WRITE_ERR_IO; + } + + try { + bool const ok = + _trust_anchor.queue_update_last_hash(src, count); + if (!ok) { + return WRITE_ERR_IO; + } + _state = State::PENDING_WRITE_ACK; + } catch (...) { + return WRITE_ERR_INVALID; + } + + _trust_anchor.execute(); + out_count = count; + return WRITE_OK; + } + + bool read_ready() override { return true; } + }; + + public: + + Hashsum_file_system(Trust_anchor &ta) + : + Single_file_system { Node_type::TRANSACTIONAL_FILE, type_name(), + Node_rwx::ro(), Xml_node("") }, + _trust_anchor { ta } + { } + + static char const *type_name() { return "hashsum"; } + + char const *type() override { return type_name(); } + + /********************************* + ** Directory-service interface ** + *********************************/ + + Open_result open(char const *path, unsigned, + Vfs::Vfs_handle **out_handle, + Genode::Allocator &alloc) override + { + if (!_single_file(path)) + + return OPEN_ERR_UNACCESSIBLE; + + try { + *out_handle = + new (alloc) Hashsum_handle(*this, *this, alloc, + _trust_anchor); + return OPEN_OK; + } + catch (Genode::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; } + catch (Genode::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; } + } + + Stat_result stat(char const *path, Stat &out) override + { + Stat_result result = Single_file_system::stat(path, out); + return result; + } + + /******************************** + ** File I/O service interface ** + ********************************/ + + Ftruncate_result ftruncate(Vfs::Vfs_handle *, file_size) override + { + return FTRUNCATE_OK; + } +}; + + +class Vfs_cbe_trust_anchor::Generate_key_file_system : public Vfs::Single_file_system +{ + private: + + Trust_anchor &_trust_anchor; + + struct Gen_key_handle : Single_vfs_handle + { + Trust_anchor &_trust_anchor; + + enum class State { NONE, PENDING }; + State _state { State::NONE, }; + + Gen_key_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Trust_anchor &ta) + : + Single_vfs_handle { ds, fs, alloc, 0 }, + _trust_anchor { ta } + { } + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + if (_state == State::NONE) { + + if (!_trust_anchor.queue_generate_key()) { + return READ_QUEUED; + } + _state = State::PENDING; + } + + (void)_trust_anchor.execute(); + + Trust_anchor::Complete_request const cr = + _trust_anchor.complete_generate_key(dst, count); + if (!cr.valid) { + return READ_QUEUED; + } + + _state = State::NONE; + out_count = count; + return cr.success ? READ_OK : READ_ERR_IO; + } + + Write_result write(char const *, file_size , file_size &) override + { + return WRITE_ERR_IO; + } + + bool read_ready() override { return true; } + }; + + public: + + Generate_key_file_system(Trust_anchor &ta) + : + Single_file_system { Node_type::TRANSACTIONAL_FILE, type_name(), + Node_rwx::ro(), Xml_node("") }, + _trust_anchor { ta } + { } + + static char const *type_name() { return "generate_key"; } + + char const *type() override { return type_name(); } + + /********************************* + ** Directory-service interface ** + *********************************/ + + Open_result open(char const *path, unsigned, + Vfs::Vfs_handle **out_handle, + Genode::Allocator &alloc) override + { + if (!_single_file(path)) + + return OPEN_ERR_UNACCESSIBLE; + + try { + *out_handle = + new (alloc) Gen_key_handle(*this, *this, alloc, + _trust_anchor); + return OPEN_OK; + } + catch (Genode::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; } + catch (Genode::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; } + } + + Stat_result stat(char const *path, Stat &out) override + { + Stat_result result = Single_file_system::stat(path, out); + return result; + } + + /******************************** + ** File I/O service interface ** + ********************************/ + + Ftruncate_result ftruncate(Vfs::Vfs_handle *, file_size) override + { + return FTRUNCATE_OK; + } +}; + + +class Vfs_cbe_trust_anchor::Encrypt_file_system : public Vfs::Single_file_system +{ + private: + + Trust_anchor &_trust_anchor; + + struct Encrypt_handle : Single_vfs_handle + { + Trust_anchor &_trust_anchor; + + enum State { NONE, PENDING }; + State _state; + + Encrypt_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Trust_anchor &ta) + : + Single_vfs_handle { ds, fs, alloc, 0 }, + _trust_anchor { ta }, + _state { State::NONE } + { } + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + if (_state != State::PENDING) { + return READ_ERR_IO; + } + + _trust_anchor.execute(); + + try { + Trust_anchor::Complete_request const cr = + _trust_anchor.complete_encrypt_key(dst, count); + if (!cr.valid) { + return READ_QUEUED; + } + + _state = State::NONE; + + out_count = count; + return cr.success ? READ_OK : READ_ERR_IO; + } catch (...) { + return READ_ERR_INVALID; + } + + return READ_ERR_IO; + } + + Write_result write(char const *src, file_size count, + file_size &out_count) override + { + if (_state != State::NONE) { + return WRITE_ERR_IO; + } + + try { + bool const ok = + _trust_anchor.queue_encrypt_key(src, count); + if (!ok) { + return WRITE_ERR_IO; + } + _state = State::PENDING; + } catch (...) { + return WRITE_ERR_INVALID; + } + + _trust_anchor.execute(); + out_count = count; + return WRITE_OK; + } + + bool read_ready() override { return true; } + }; + + public: + + Encrypt_file_system(Trust_anchor &ta) + : + Single_file_system { Node_type::TRANSACTIONAL_FILE, type_name(), + Node_rwx::rw(), Xml_node("") }, + _trust_anchor { ta } + { } + + static char const *type_name() { return "encrypt"; } + + char const *type() override { return type_name(); } + + /********************************* + ** Directory-service interface ** + *********************************/ + + Open_result open(char const *path, unsigned, + Vfs::Vfs_handle **out_handle, + Genode::Allocator &alloc) override + { + if (!_single_file(path)) + return OPEN_ERR_UNACCESSIBLE; + + try { + *out_handle = + new (alloc) Encrypt_handle(*this, *this, alloc, + _trust_anchor); + return OPEN_OK; + } + catch (Genode::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; } + catch (Genode::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; } + } + + Stat_result stat(char const *path, Stat &out) override + { + Stat_result result = Single_file_system::stat(path, out); + return result; + } + + /******************************** + ** File I/O service interface ** + ********************************/ + + Ftruncate_result ftruncate(Vfs::Vfs_handle *, file_size) override + { + return FTRUNCATE_OK; + } +}; + + +class Vfs_cbe_trust_anchor::Decrypt_file_system : public Vfs::Single_file_system +{ + private: + + Trust_anchor &_trust_anchor; + + struct Decrypt_handle : Single_vfs_handle + { + Trust_anchor &_trust_anchor; + + enum State { NONE, PENDING }; + State _state; + + Decrypt_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Trust_anchor &ta) + : + Single_vfs_handle { ds, fs, alloc, 0 }, + _trust_anchor { ta }, + _state { State::NONE } + { } + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + if (_state != State::PENDING) { + return READ_ERR_IO; + } + + _trust_anchor.execute(); + + try { + Trust_anchor::Complete_request const cr = + _trust_anchor.complete_decrypt_key(dst, count); + if (!cr.valid) { + return READ_QUEUED; + } + + _state = State::NONE; + + out_count = count; + return cr.success ? READ_OK : READ_ERR_IO; + } catch (...) { + return READ_ERR_INVALID; + } + + return READ_ERR_IO; + } + + Write_result write(char const *src, file_size count, + file_size &out_count) override + { + if (_state != State::NONE) { + return WRITE_ERR_IO; + } + + try { + bool const ok = + _trust_anchor.queue_decrypt_key(src, count); + if (!ok) { + return WRITE_ERR_IO; + } + _state = State::PENDING; + } catch (...) { + return WRITE_ERR_INVALID; + } + + _trust_anchor.execute(); + out_count = count; + return WRITE_OK; + } + + bool read_ready() override { return true; } + }; + + public: + + Decrypt_file_system(Trust_anchor &ta) + : + Single_file_system { Node_type::TRANSACTIONAL_FILE, type_name(), + Node_rwx::rw(), Xml_node("") }, + _trust_anchor { ta } + { } + + static char const *type_name() { return "decrypt"; } + + char const *type() override { return type_name(); } + + /********************************* + ** Directory-service interface ** + *********************************/ + + Open_result open(char const *path, unsigned, + Vfs::Vfs_handle **out_handle, + Genode::Allocator &alloc) override + { + if (!_single_file(path)) + return OPEN_ERR_UNACCESSIBLE; + + try { + *out_handle = + new (alloc) Decrypt_handle(*this, *this, alloc, + _trust_anchor); + return OPEN_OK; + } + catch (Genode::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; } + catch (Genode::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; } + } + + Stat_result stat(char const *path, Stat &out) override + { + Stat_result result = Single_file_system::stat(path, out); + return result; + } + + /******************************** + ** File I/O service interface ** + ********************************/ + + Ftruncate_result ftruncate(Vfs::Vfs_handle *, file_size) override + { + return FTRUNCATE_OK; + } +}; + + +class Vfs_cbe_trust_anchor::Initialize_file_system : public Vfs::Single_file_system +{ + private: + + Trust_anchor &_trust_anchor; + + struct Initialize_handle : Single_vfs_handle + { + Trust_anchor &_trust_anchor; + + enum class State { NONE, PENDING }; + State _state { State::NONE }; + + bool _init_pending { false }; + + Initialize_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Trust_anchor &ta) + : + Single_vfs_handle { ds, fs, alloc, 0 }, + _trust_anchor { ta } + { } + + Read_result read(char *, file_size, file_size &) override + { + if (_state != State::PENDING) { + return READ_ERR_INVALID; + } + + (void)_trust_anchor.execute(); + + Trust_anchor::Complete_request const cr = + _init_pending ? _trust_anchor.complete_queue_unlock() + : _trust_anchor.complete_queue_initialize(); + if (!cr.valid) { + return READ_QUEUED; + } + + _state = State::NONE; + _init_pending = false; + + return cr.success ? READ_OK : READ_ERR_IO; + } + + Write_result write(char const *src, file_size count, + file_size &out_count) override + { + if (_state != State::NONE) { + return WRITE_ERR_INVALID; + } + + _init_pending = _trust_anchor.initialized(); + + bool const res = _init_pending ? _trust_anchor.queue_unlock(src, count) + : _trust_anchor.queue_initialize(src, count); + + if (!res) { + return WRITE_ERR_IO; + } + _state = State::PENDING; + + out_count = count; + return WRITE_OK; + } + + bool read_ready() override { return true; } + }; + + public: + + Initialize_file_system(Trust_anchor &ta) + : + Single_file_system { Node_type::TRANSACTIONAL_FILE, type_name(), + Node_rwx::rw(), Xml_node("") }, + _trust_anchor { ta } + { } + + static char const *type_name() { return "initialize"; } + + char const *type() override { return type_name(); } + + /********************************* + ** Directory-service interface ** + *********************************/ + + Open_result open(char const *path, unsigned, + Vfs::Vfs_handle **out_handle, + Genode::Allocator &alloc) override + { + if (!_single_file(path)) + + return OPEN_ERR_UNACCESSIBLE; + + try { + *out_handle = + new (alloc) Initialize_handle(*this, *this, alloc, + _trust_anchor); + return OPEN_OK; + } + catch (Genode::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; } + catch (Genode::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; } + } + + Stat_result stat(char const *path, Stat &out) override + { + Stat_result result = Single_file_system::stat(path, out); + return result; + } + + /******************************** + ** File I/O service interface ** + ********************************/ + + Ftruncate_result ftruncate(Vfs::Vfs_handle *, file_size) override + { + return FTRUNCATE_OK; + } +}; + + +struct Vfs_cbe_trust_anchor::Local_factory : File_system_factory +{ + Trust_anchor _trust_anchor; + + Decrypt_file_system _decrypt_fs; + Encrypt_file_system _encrypt_fs; + Generate_key_file_system _gen_key_fs; + Hashsum_file_system _hash_fs; + Initialize_file_system _init_fs; + + using Storage_path = String<256>; + static Storage_path _storage_path(Xml_node const &node) + { + if (!node.has_attribute("storage_dir")) { + error("mandatory 'storage_dir' attribute missing"); + struct Missing_storage_dir_attribute { }; + throw Missing_storage_dir_attribute(); + } + return node.attribute_value("storage_dir", Storage_path()); + } + + Local_factory(Vfs::Env &vfs_env, Xml_node config) + : + _trust_anchor { vfs_env, _storage_path(config).string() }, + _decrypt_fs { _trust_anchor }, + _encrypt_fs { _trust_anchor }, + _gen_key_fs { _trust_anchor }, + _hash_fs { _trust_anchor }, + _init_fs { _trust_anchor } + { } + + Vfs::File_system *create(Vfs::Env&, Xml_node node) override + { + if (node.has_type(Decrypt_file_system::type_name())) { + return &_decrypt_fs; + } + + if (node.has_type(Encrypt_file_system::type_name())) { + return &_encrypt_fs; + } + + if (node.has_type(Generate_key_file_system::type_name())) { + return &_gen_key_fs; + } + + if (node.has_type(Hashsum_file_system::type_name())) { + return &_hash_fs; + } + + if (node.has_type(Initialize_file_system::type_name())) { + return &_init_fs; + } + + return nullptr; + } +}; + + +class Vfs_cbe_trust_anchor::File_system : private Local_factory, + public Vfs::Dir_file_system +{ + private: + + using Config = String<128>; + + static Config _config(Xml_node const &node) + { + char buf[Config::capacity()] { }; + + Xml_generator xml(buf, sizeof (buf), "dir", [&] () { + xml.attribute("name", node.attribute_value("name", String<32>(""))); + + xml.node("decrypt", [&] () { }); + xml.node("encrypt", [&] () { }); + xml.node("generate_key", [&] () { }); + xml.node("hashsum", [&] () { }); + xml.node("initialize", [&] () { }); + }); + + return Config(Cstring(buf)); + } + + public: + + File_system(Vfs::Env &vfs_env, Genode::Xml_node node) + : + Local_factory { vfs_env, node }, + Vfs::Dir_file_system { vfs_env, Xml_node(_config(node).string()), *this } + { } + + ~File_system() { } +}; + + +/************************** + ** VFS plugin interface ** + **************************/ + +extern "C" Vfs::File_system_factory *vfs_file_system_factory(void) +{ + struct Factory : Vfs::File_system_factory + { + Vfs::File_system *create(Vfs::Env &vfs_env, + Genode::Xml_node node) override + { + try { + return new (vfs_env.alloc()) + Vfs_cbe_trust_anchor::File_system(vfs_env, node); + + } catch (...) { + Genode::error("could not create 'cbe_trust_anchor'"); + } + return nullptr; + } + }; + + static Factory factory; + return &factory; +} diff --git a/tool/autopilot.list b/tool/autopilot.list index 99c45471a1..66b83f1ae2 100644 --- a/tool/autopilot.list +++ b/tool/autopilot.list @@ -1,5 +1,6 @@ aes_cbc_4k bomb +cbe_tester cpu_bench cpu_quota cpu_sampler diff --git a/tool/cbe_autopilot b/tool/cbe_autopilot new file mode 100755 index 0000000000..05a6df050c --- /dev/null +++ b/tool/cbe_autopilot @@ -0,0 +1,199 @@ +#!/usr/bin/make -sf + +# +# \brief Tool to GNAT-prove and test the Consistent Block Encrypter (CBE) +# \author Martin Stein +# \date 2020-11-10 +# + + +###################### +# User configuration # +###################### + +DEFAULT_GENODE_DIR := $(realpath $(dir $(MAKEFILE_LIST))/..) +DEFAULT_BUILD_DIR := $(DEFAULT_GENODE_DIR)/.build.cbe_autopilot +DEFAULT_DEPOT_USER := cbe_autopilot +DEFAULT_DEPOT_DIR := $(DEFAULT_GENODE_DIR)/.depot.cbe_autopilot +DEFAULT_JOBS := $(shell nproc) +DEFAULT_CROSS_DEV_PREFIX := genode-x86- + +GENODE_DIR ?= $(DEFAULT_GENODE_DIR) +BUILD_DIR ?= $(DEFAULT_BUILD_DIR) +DEPOT_USER ?= $(DEFAULT_DEPOT_USER) +DEPOT_DIR ?= $(DEFAULT_DEPOT_DIR) +JOBS ?= $(DEFAULT_JOBS) +CROSS_DEV_PREFIX ?= $(DEFAULT_CROSS_DEV_PREFIX) +VERBOSE ?= @ + + +########################## +# Internal configuration # +########################## + +ARCH := x86_64 +CONTRIB_DIR := $(GENODE_DIR)/contrib +BUILD_CONF := $(BUILD_DIR)/etc/build.conf +TOOLS_CONF := $(BUILD_DIR)/etc/tools.conf +CBE_PROJECT_FILE := $(BUILD_DIR)/cbe.gpr +LSC_INSTALL_DIR := $(BUILD_DIR)/libsparkcrypto_install + + +#################### +# Port directories # +#################### + +REPOSITORIES += $(GENODE_DIR)/repos/base +REPOSITORIES += $(GENODE_DIR)/repos/base-nova +REPOSITORIES += $(GENODE_DIR)/repos/base-linux +REPOSITORIES += $(GENODE_DIR)/repos/os +REPOSITORIES += $(GENODE_DIR)/repos/libports +REPOSITORIES += $(GENODE_DIR)/repos/ports +REPOSITORIES += $(GENODE_DIR)/repos/dde_linux +REPOSITORIES += $(GENODE_DIR)/repos/gems + +include $(GENODE_DIR)/repos/base/mk/util.inc + +PORTS := \ + ada-runtime bash coreutils curl dde_linux libc libiconv libsparkcrypto \ + libssh ncurses nova openssl qemu-usb stdcxx virtualbox5 zlib stb \ + ttf-bitstream-vera cbe + +MISSING_PORTS := \ + $(strip $(foreach PORT,$(PORTS),$(if $(call select_from_ports,$(PORT)),,$(PORT)))) + +CBE_DIR := $(call select_from_ports,cbe)/cbe +LSC_DIR := $(call select_from_ports,libsparkcrypto)/libsparkcrypto + + +############ +# Commands # +############ + +BUILD_DIR_MAKE := \ + CROSS_DEV_PREFIX=$(CROSS_DEV_PREFIX) \ + CONTRIB_DIR=$(CONTRIB_DIR) \ + DEPOT_DIR=$(DEPOT_DIR) \ + KERNEL=linux \ + BOARD=linux \ + $(MAKE) -sC $(BUILD_DIR) + +DEPOT_CREATE := \ + CROSS_DEV_PREFIX=$(CROSS_DEV_PREFIX) \ + CONTRIB_DIR=$(CONTRIB_DIR) \ + DEPOT_DIR=$(DEPOT_DIR) \ + UPDATE_VERSIONS=1 \ + FORCE=1 \ + REBUILD= \ + $(GENODE_DIR)/tool/depot/create -j$(JOBS) + +CREATE_BUILD_DIR := \ + BUILD_DIR=$(BUILD_DIR) \ + $(GENODE_DIR)/tool/create_builddir + +GNAT_PROVE_FLAGS := \ + --mode=flow \ + -j0 \ + --prover=z3,cvc4 \ + --steps=1000 \ + --memlimit=1000 \ + --checks-as-errors \ + --warnings=error \ + -U \ + -P + +GNAT_PROVE := gnatprove $(GNAT_PROVE_FLAGS) + + +########### +# Targets # +########### + +usage: + @echo + @echo "Tool to GNAT-prove and test the Consistent Block Encrypter (CBE)" + @echo + @echo "Usage:" + @echo + @echo " $(firstword $(notdir $(MAKEFILE_LIST))) [clean|prove|basics]" + @echo + @echo "Considered environment variables:" + @echo "" + @echo " VERBOSE= Show individual operations." + @echo "" + @echo " JOBS=4 Number of jobs to execute in parallel." + @echo " Defaults to '$(DEFAULT_JOBS)'." + @echo "" + @echo " GENODE_DIR=~/genode Base directory of Genode repository." + @echo " Defaults to '$(DEFAULT_GENODE_DIR)'." + @echo "" + @echo " DEPOT_USER=shrimp Genode depot user-name." + @echo " Defaults to '$(DEFAULT_DEPOT_USER)'." + @echo "" + @echo " DEPOT_DIR=~/depot Base directory of Genode depot." + @echo " Defaults to '$(DEFAULT_DEPOT_DIR)'." + @echo "" + @echo " BUILD_DIR=~/build Genode build directory." + @echo " Defaults to '$(DEFAULT_BUILD_DIR)'." + @echo "" + @echo " CROSS_DEV_PREFIX=~/bin/genode-x86- GCC command prefix." + @echo " Defaults to '$(DEFAULT_CROSS_DEV_PREFIX)'." + +ifneq ($(MISSING_PORTS),) +checked_ports_exist: + @echo "" + @echo "Error: Ports not prepared or outdated:" + @echo " $(MISSING_PORTS)" + @echo "" + @echo "You can prepare respectively update them as follows:" + @echo " $(GENODE_DIR)/tool/ports/prepare_port $(MISSING_PORTS) -j$(JOBS)" + @echo "" + @false +else +checked_ports_exist: +endif + +RUN_OPT_DAU := RUN_OPT += --depot-auto-update + +$(BUILD_DIR): + $(VERBOSE)$(CREATE_BUILD_DIR) $(ARCH) + $(VERBOSE)sed -i 's/^#REPOSITORIES +=/REPOSITORIES +=/g' $(BUILD_CONF) + $(VERBOSE)sed -i 's/^CONTRIB_DIR :=.*$$//g' $(BUILD_CONF) + $(VERBOSE)sed -i 's/^#MAKE += -j4$$/MAKE += -j$(JOBS)/g' $(BUILD_CONF) + $(VERBOSE)sed -i 's/^#$(RUN_OPT_DAU)$$/$(RUN_OPT_DAU)/g' $(BUILD_CONF) + $(VERBOSE)echo 'REPOSITORIES += $$(GENODE_DIR)/repos/cbe' >> $(BUILD_CONF) + $(VERBOSE)echo 'RUN_OPT += --autopilot' >> $(BUILD_CONF) + $(VERBOSE)echo 'RUN_OPT += --depot-user $(DEPOT_USER)' >> $(BUILD_CONF) + $(VERBOSE)echo 'CROSS_DEV_PREFIX=$(CROSS_DEV_PREFIX)' > $(TOOLS_CONF) + +$(CBE_PROJECT_FILE): $(BUILD_DIR) checked_ports_exist + $(VERBOSE)echo 'with "$(LSC_INSTALL_DIR)/libsparkcrypto";' > $(CBE_PROJECT_FILE) + $(VERBOSE)echo 'project CBE is' >> $(CBE_PROJECT_FILE) + $(VERBOSE)echo ' for Create_Missing_Dirs use "True";' >> $(CBE_PROJECT_FILE) + $(VERBOSE)echo ' for Object_Dir use "$(BUILD_DIR)/cbe_gpr_object_dir";' >> $(CBE_PROJECT_FILE) + $(VERBOSE)echo ' for Source_Dirs use ("$(CBE_DIR)/src/lib/sha256_4k", "$(CBE_DIR)/src/lib/cbe", "$(CBE_DIR)/src/lib/cbe_common");' >> $(CBE_PROJECT_FILE) + $(VERBOSE)echo ' for Library_Kind use "static";' >> $(CBE_PROJECT_FILE) + $(VERBOSE)echo ' for Library_Name use "cbe";' >> $(CBE_PROJECT_FILE) + $(VERBOSE)echo ' -- show full paths' >> $(CBE_PROJECT_FILE) + $(VERBOSE)echo ' package Compiler is' >> $(CBE_PROJECT_FILE) + $(VERBOSE)echo ' for Switches ("Ada") use ("-gnatef");' >> $(CBE_PROJECT_FILE) + $(VERBOSE)echo ' end Compiler;' >> $(CBE_PROJECT_FILE) + $(VERBOSE)echo 'end CBE;' >> $(CBE_PROJECT_FILE) + +$(LSC_INSTALL_DIR): $(BUILD_DIR) checked_ports_exist + $(VERBOSE)$(MAKE) -sC $(LSC_DIR) NO_SPARK=1 DESTDIR=$(LSC_INSTALL_DIR) install + +.PHONY: prove +prove: $(LSC_INSTALL_DIR) $(CBE_PROJECT_FILE) checked_ports_exist + $(VERBOSE)$(GNAT_PROVE) $(CBE_PROJECT_FILE) + +.PHONY: basics +basics: $(BUILD_DIR) prove checked_ports_exist + $(VERBOSE)$(DEPOT_CREATE) $(DEPOT_USER)/pkg/x86_64/cbe_demo + $(VERBOSE)$(BUILD_DIR_MAKE) run/cbe_tester + $(VERBOSE)$(BUILD_DIR_MAKE) run/vfs_cbe_init + $(VERBOSE)$(BUILD_DIR_MAKE) run/vfs_cbe + +.PHONY: clean +clean: + $(VERBOSE)rm -rf $(BUILD_DIR) $(DEPOT_DIR)