/**
 * @file       tlb.h
 * @brief      Convert REE virtual user space address to physical using reading TLB
 * @author     Ivan Vorobiov (i.vorobiov@samsung.com)
 * @version    1.0
 * @date       Created Jul 13, 2016
 * @copyright  In Samsung Ukraine R&D Center (SURC) under a contract between
 * @copyright  LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine) and
 * @copyright  "Samsung Electronics Co", Ltd (Seoul, Republic of Korea)
 * @copyright  Copyright: (c) Samsung Electronics Co, Ltd 2016. All rights reserved.
**/

#ifndef PA_TZ_DRV_SRC_TLB_H_
#define PA_TZ_DRV_SRC_TLB_H_

#include "memory.h"


#define ADDR_LEVEL_MASK       (0x1FFULL)
#define ADDR_LEVEL_SHIFT      (9ULL)

#define PTRS_PER_PTE          (48ULL)
#define PTRS_PER_PMD          (PTRS_PER_PTE)
#define PTRS_PER_PUD          (PTRS_PER_PTE)
#define PTRS_PER_PGD          (PTRS_PER_PTE)
#define PTRS_LEVEL_MASK(PTRS) ((1ULL << (PTRS)) - 1ULL)

#define PGD_LEVEL_SHIFT       ((PAGE_SHIFT) + 3 * (ADDR_LEVEL_SHIFT))
#define PUD_LEVEL_SHIFT       ((PAGE_SHIFT) + 2 * (ADDR_LEVEL_SHIFT))
#define PMD_LEVEL_SHIFT       ((PAGE_SHIFT) + (ADDR_LEVEL_SHIFT))
#define PTE_LEVEL_SHIFT       (PAGE_SHIFT)

enum {
  kNumTable = 3,  //!< Number tables contain PUD, PMD, PTE
  kSizeTable = (1 << ADDR_LEVEL_SHIFT)  //!< Size table for PUD, PMD, PTE (usually 512)
};

typedef struct {
  uint8_t read:1;
  uint8_t write:1;
  uint8_t exec:1;
} AccessPermissionFlags;

/**
 * @brief TLB converter structure
 */
typedef struct {
  KernelAddress   pgd;                    //!< Virtual address of PGD in kernel space
  PhysicalAddress phys_pgd;               //!< Physical address of PGD
  uint32_t index_table[kNumTable];        //!< Offset addresses of PUD, PMD, PTE
  PhysicalAddress buffer[kNumTable][kSizeTable]; //!< The buffer contains
                                                 //!< addresses of tables TLB and it must be aligned
                                                 //!< on physical address or an exception may occure.
} TlbConverterInfo;

/**
 * @brief Get physical address from table TLB or convert again
 * @param [in,out] context TLB converter context.
 * @param [in] pgd_address Virtual address of PGD in kernel space
 * @param [in] user_virt Start virtual address in user space
 * @param [out] phys Physical address
 * @return ::PA_TZ_SUCCESS in case of success, ::PA_TZ_GENERAL_ERROR
 */
PaTzResult TaskAddressToPhysical(TlbConverterInfo* context, KernelAddress pgd_address,
                                 ProcessAddress user_virt, PhysicalAddress *phys,
                                 AccessPermissionFlags* flags);

#endif // PA_TZ_DRV_SRC_TLB_H_
