/*
    This file is part of MutekH.
    
    MutekH is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as
    published by the Free Software Foundation; version 2.1 of the
    License.
    
    MutekH is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
    
    You should have received a copy of the GNU Lesser General Public
    License along with MutekH; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301 USA.

    Copyright Jeremie Brunel <jeremie.brunel@telecom-paristech.fr> (c) 2014

*/

#ifndef __HSM_H
#define __HSM_H

#include <hexo/types.h>
#include <hexo/error.h>
#include <hexo/gpct_platform_hexo.h>
#include <gpct/cont_clist.h>

#include <device/driver.h>
#include "sb_hsm_types.h"

struct dev_hsm_rq_s;

#define DRIVER_CLASS_HSM 672

/**
  HSM device read/write callback
*/
#define DEVHSM_CALLBACK(n) void (n) (const struct dev_hsm_rq_s *rq)

/**
  HSM device callback. Parameters:
  @param rq pointer to HSM request.
*/
typedef DEVHSM_CALLBACK(devhsm_callback_t);

/**
  HSM request type
*/
enum hsm_rq_op_e
{
  HSM_NEW_PSPE,    //< New/update pspe
  HSM_DELETE_PSPE, //< Delete pspe
  HSM_NEW_SP,      //< New/update sp
  HSM_DELETE_SP,   //< Delete sp
  HSM_INTEGRITY,   //< Integrity initialization
  HSM_READ,        //<  Atomic read
  HSM_WRITE,       //< Atomic write
};

/**
  Protection mode. Read-only (that is, written once, read many) memory pages are encrypted by the DESX block cipher in counter mode and their integrity is protected by sets of DESX CBC MACs. Read-write memory pages are encrypted by the DESX block cipher in CBC mode and their integrity is protected by trees of DESX CBC MACs.
*/
enum hsm_prot_mode_e
{
  HSM_PROT_RW, //< Read-Write
  HSM_PROT_RO, //< Read-Only
};

/**
  HSM request for PSPE operations
*/
struct hsm_drv_pspe_s
{
  paddr_t addr;             //< PSPE address
  paddr_t integrity_page;   //< Byte address of integrity page
  uint8_t integrity_idx;    //< Index of MAC set (0-4) or MAC tree (0-3) in integrity page
  paddr_t iv_page;          //< Byte address of IV page
  uint8_t iv_idx;           //< Index of IV set (0-4) in IV page
  uint16_t sp_idx;          //< Index of security policy
  bool_t unprotected;       //< Set if the unprotected memory page
  enum sb_page_size_e size; //< Size of memory page
};

/**
  HSM request for SP operations
*/
struct hsm_drv_sp_s
{
  paddr_t addr;              // < SP address
  bool_t c;                  // < Confidentiality flag
  bool_t i;                  // < Integrity flag
  enum hsm_prot_mode_e mode; // < Read-write or read-only
  uint32_t *key_c;           // < Confidentiality key
  uint32_t *key_i;           // < Integrity key
};

/**
  HSM request for integrity initialization
*/
struct hsm_drv_integrity_s
{
  paddr_t addr; // < Address of protected memory page
  bool_t new;   // < True if new integrity page (that is, first MAC tree of page of MAC trees)
};

/**
  HSM request for atomic group read/write
*/
struct hsm_drv_group_s
{
  paddr_t src;  // < Source address
  paddr_t dest; // < Destination address
  bool_t read_ok;  // < used to know if for the current request the reading is done
};

/**
  HSM request @see devhsm_request_t
*/
struct dev_hsm_rq_s
{
  enum hsm_rq_op_e type; //< Operation type

  union
  {
    struct hsm_drv_pspe_s pspe;           // < PSPE operation
    struct hsm_drv_sp_s sp;               // < SP operation
    struct hsm_drv_integrity_s integrity; // < Integrity initialization
    struct hsm_drv_group_s group;         // < Atomic group read/write
  }; 

  error_t error;                           // < Error code set by driver

  devhsm_callback_t *callback;             // < Callback function
  void              *pvdata;               // < Private data for callback

  const struct device_hsm_s *hdev;         // < Associated HSM device
  void                      *drvdata;      // < Driver private data

  CONTAINER_ENTRY_TYPE(CLIST) queue_entry; // < Chaining of HSM requests
};

CONTAINER_TYPE(dev_hsm_queue, CLIST, struct dev_hsm_rq_s, queue_entry);
CONTAINER_FUNC(dev_hsm_queue, CLIST, static inline, dev_hsm_queue);

/**
  Macro for declaring functions that process requests to devices of class HSM
*/
#define DEVHSM_REQUEST(n) error_t (n) (const struct device_hsm_s *hdev, struct dev_hsm_rq_s *rq)

/**
  Function pointer to function that enqueues requests to HSM. Parameters:
  @param hdev Pointer to device descriptor
  @param rq Pointer to request. Type and associated structure must be initialized.
  @returns Zero.
*/
typedef DEVHSM_REQUEST(devhsm_request_t);

DRIVER_CLASS_TYPES(hsm, devhsm_request_t *f_request);

/**
  Enqueues a PSPE init request. If OS scheduler enabled puts current context in wait state until HSM operation completes, else spins until HSM operation completes. Parameters:
  @param hdev Pointer to device descriptor.
  @param pspe_addr Byte address of target PSPE.
  @param integrity_page Byte address of page of MAC sets or MAC trees (ignored if no integrity protection).
  @param integrity_idx Index of MAC set (0-4) or MAC tree (0-3) in page of MAC sets or MAC trees (ignored if no integrity protection).
  @param iv_page Byte address of page of IV sets (ignored if no CBC confidentiality or IV disabled).
  @param iv_idx Index of IV set (0-4) in page of IV sets (ignored if no CBC confidentiality or IV disabled).
  @param sp_idx Index of Security Policy in array of SPs in Master Block.
  @param unprotected Set if associated SP specifies confidentiality: none and integrity: none. Redundant, used to avoid fetching useless SPs.
  @param size Size of memory page associated to newly created PSPE @see sb_page_size_e.
  @returns Zero.
*/
error_t dev_hsm_wait_new_pspe(const struct device_hsm_s *hdev,
			      paddr_t pspe_addr, paddr_t integrity_page,
			      uint8_t integrity_idx, paddr_t iv_page,
			      uint8_t iv_idx, uint16_t sp_idx,
			      bool_t unprotected, enum sb_page_size_e size);

/**
  Enqueues a PSPE init request. Spins until HSM operation completes. Parameters:
  @param hdev Pointer to device descriptor.
  @param pspe_addr Byte address of target PSPE.
  @param integrity_page Byte address of page of MAC sets or MAC trees (ignored if no integrity protection).
  @param integrity_idx Index of MAC set (0-4) or MAC tree (0-3) in page of MAC sets or MAC trees (ignored if no integrity protection).
  @param iv_page Byte address of page of IV sets (ignored if no CBC confidentiality or IV disabled).
  @param iv_idx Index of IV set (0-4) in page of IV sets (ignored if no CBC confidentiality or IV disabled).
  @param sp_idx Index of Security Policy in array of SPs in Master Block.
  @param unprotected Set if associated SP specifies confidentiality: none and integrity: none. Redundant, used to avoid fetching useless SPs.
  @param size Size of memory page associated to newly created PSPE @see sb_page_size_e.
  @returns Zero.
*/
error_t dev_hsm_lock_new_pspe(const struct device_hsm_s *hdev,
			      paddr_t pspe_addr, paddr_t integrity_page,
			      uint8_t integrity_idx, paddr_t iv_page,
			      uint8_t iv_idx, uint16_t sp_idx,
			      bool_t unprotected, enum sb_page_size_e size);

/**
  Enqueues a PSPE invalidate request. If OS scheduler enabled puts current context in wait state until HSM operation completes, else spins until HSM operation completes. Parameters:
  @param hdev Pointer to device descriptor.
  @param pspe_addr Byte address of target PSPE.
  @returns Zero.
*/
error_t dev_hsm_wait_delete_pspe(const struct device_hsm_s *hdev,
				 paddr_t pspe_addr);

/**
  Enqueues a PSPE invalidate request. Spins until HSM operation completes. Parameters:
  @param hdev Pointer to device descriptor.
  @param pspe_addr Byte address of target PSPE.
  @returns Zero.
*/
error_t dev_hsm_lock_delete_pspe(const struct device_hsm_s *hdev,
				 paddr_t pspe_addr);

/**
  Enqueues a SP init request. If OS scheduler enabled puts current context in wait state until HSM operation completes, else spins until HSM operation completes. Parameters:
  @param hdev Pointer to device descriptor.
  @param sp_addr Byte address of target SP.
  @param c Confidentiality mode. Memory pages bound to this SP are encrypted if true.
  @param i Integrity mode. Memory pages bound to this SP have their integrity managed and checked if true.
  @param mode Page type. Specifies whether memory pages bound to this SP are read-only or read-write @see hsm_prot_mode_e.
  @param key_c 184-bits DES-X key used for encryption. k=key_c[1](23..0)||key_c[0], k1=key_c[3]||key_c[2], k2=key_c[5]||key_c[6]. Most significant byte of key_c[1] ignored.
  @param key_i 184-bits DES-X key used for RO-pages integrity. k=key_i[1](23..0)||key_i[0], k1=key_i[3]||key_i[2], k2=key_i[5]||key_i[6]. Most significant byte of key_i[1] ignored.
  @returns Zero.
*/
error_t dev_hsm_wait_new_sp(const struct device_hsm_s *hdev,
			    paddr_t sp_addr, bool_t c, bool_t i,
			    enum hsm_prot_mode_e mode,
			    uint32_t *key_c, uint32_t *key_i);

/**
  Enqueues a SP init request. Spins until HSM operation completes. Parameters:
  @param hdev Pointer to device descriptor.
  @param sp_addr Byte address of target SP.
  @param c Confidentiality mode. Memory pages bound to this SP are encrypted if true.
  @param i Integrity mode. Memory pages bound to this SP have their integrity managed and checked if true.
  @param mode Page type. Specifies whether memory pages bound to this SP are read-only or read-write @see hsm_prot_mode_e.
  @param key_c 184-bits DES-X key used for encryption. k=key_c[1](23..0)||key_c[0], k1=key_c[3]||key_c[2], k2=key_c[5]||key_c[6]. Most significant byte of key_c[1] ignored.
  @param key_i 184-bits DES-X key used for RO-pages integrity. k=key_i[1](23..0)||key_i[0], k1=key_i[3]||key_i[2], k2=key_i[5]||key_i[6]. Most significant byte of key_i[1] ignored.
  @returns Zero.
*/
error_t dev_hsm_lock_new_sp(const struct device_hsm_s *hdev,
			    paddr_t sp_addr, bool_t c, bool_t i,
			    enum hsm_prot_mode_e mode,
			    uint32_t *key_c, uint32_t *key_i);

/**
  Enqueues a SP invalidate request. If OS scheduler enabled puts current context in wait state until HSM operation completes, else spins until HSM operation completes. Parameters:
  @param hdev Pointer to device descriptor.
  @param sp_addr Byte address of target SP.
  @returns Zero.
*/
error_t dev_hsm_wait_delete_sp(const struct device_hsm_s *hdev,
			       paddr_t sp_addr);

/**
  Enqueues a SP invalidate request. Spins until HSM operation completes. Parameters:
  @param hdev Pointer to device descriptor.
  @param sp_addr Byte address of target SP.
  @returns Zero.
*/
error_t dev_hsm_lock_delete_sp(const struct device_hsm_s *hdev,
			       paddr_t sp_addr);

/**
  Initialize a MAC tree to protect integrity of a memory page. If OS scheduler enabled puts current context in wait state until HSM operation completes, else spins until HSM operation completes. Parameters:
  @param hdev Pointer to device descriptor.
  @param addr Byte address of memory page to protect.
  @param new If set the MAC tree is the first of its page of MAC trees and the topmost row of the other trees are not checked before computing the root of the page of MAC trees. Else, the MAC tree is not the first and the topmost row of the other trees are checked.
  @returns Zero.
*/
error_t dev_hsm_wait_integrity(const struct device_hsm_s *hdev,
			       paddr_t addr, bool_t new);

/**
  Initialize a MAC tree to protect integrity of a memory page. Spins until HSM operation completes. Parameters:
  @param hdev Pointer to device descriptor.
  @param addr Byte address of memory page to protect.
  @param new If set the MAC tree is the first of its page of MAC trees and the topmost row of the other trees are not checked before computing the root of the page of MAC trees. Else, the MAC tree is not the first and the topmost row of the other trees are checked.
  @returns Zero.
*/
error_t dev_hsm_lock_integrity(const struct device_hsm_s *hdev,
			       paddr_t addr, bool_t new);

/**
  Atomic 256 bits group read. If OS scheduler enabled puts current context in wait state until HSM operation completes, else spins until HSM operation completes. Parameters:
  @param hdev Pointer to device descriptor.
  @param src Source byte address.
  @param dest Destination byte address.
  @returns Zero.
*/
error_t dev_hsm_wait_read(const struct device_hsm_s *hdev,
			  paddr_t src, uintptr_t dest);

/**
  Atomic 256 bits group read. Spins until HSM operation completes. Parameters:
  @param hdev Pointer to device descriptor.
  @param src Source byte address.
  @param dest Destination byte address.
  @returns Zero.
*/
error_t dev_hsm_lock_read(const struct device_hsm_s *hdev,
			  paddr_t src, uintptr_t dest);

/**
  Atomic 256 bits group write. If OS scheduler enabled puts current context in wait state until HSM operation completes, else spins until HSM operation completes. Parameters:
  @param hdev Pointer to device descriptor.
  @param src Source byte address.
  @param dest Destination byte address.
  @returns Zero.
*/
error_t dev_hsm_wait_write(const struct device_hsm_s *hdev,
			   uintptr_t src, paddr_t dest);

/**
  Atomic 256 bits group write. Spins until HSM operation completes. Parameters:
  @param hdev Pointer to device descriptor.
  @param src Source byte address.
  @param dest Destination byte address.
  @returns Zero.
*/
error_t dev_hsm_lock_write(const struct device_hsm_s *hdev,
			   uintptr_t src, paddr_t dest);

void process_end_read(struct device_s *dev, struct dev_hsm_rq_s *rq);

#endif
