/*
 * \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 <base/stdint.h>
#include <base/output.h>

/* CBE includes */
#include <cbe/types.h>
#include <cbe/spark_object.h>


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_ */