/**
 * @file       memory.h
 * @brief      Platform-depended API to work with memory inside driver
 * @author     Ivan Vorobiov (i.vorobiov@samsung.com)
 * @version    1.0
 * @date       Created Jun 6, 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_MEMORY_H_
#define PA_TZ_DRV_SRC_MEMORY_H_

#include <stdint.h>
#include <limits.h>

#include "pa_tz_api.h"

/**
 * @brief Type of physical address
 */
#ifndef CONFIG_KERNEL_32
typedef uint64_t PhysicalAddress;
#else
typedef uint32_t PhysicalAddress;
#endif

/**
 * @brief Type of kernel address
 */
#ifndef CONFIG_KERNEL_32
typedef uint64_t KernelAddress;
#else
typedef uint32_t KernelAddress;
#endif

/**
 * @brief Type of user space process address
 */
#ifndef CONFIG_KERNEL_32
typedef uint64_t ProcessAddress;
#else
typedef uint32_t ProcessAddress;
#endif

/**
 * @brief Page shift/size is depended of architecture CPU
 */
#ifndef PAGE_SHIFT
#define PAGE_SHIFT (12ULL)
#endif

#ifndef PAGE_SIZE
#define PAGE_SIZE  (1ULL << (PAGE_SHIFT))
#endif

/**
 * @brief Pre-defined mask is used in aligning address of memory
 *
 * PAGE_MASK is not used here and anywhere in project, because this define
 * has different sense in SDKs/kernel/etc (11111000 or 00000111)
 */
static const uint64_t kPageMask = PAGE_SIZE - 1;

/**
 * @brief Return page address (0x1150 -> 0x1000)
 * @param [in] address_memory Address of memory
 * @return ::uint64_t
 */
static inline uint64_t PageAddress(uint64_t address_memory) {
  return (address_memory & ~kPageMask);
}

/**
 * @brief Return offset inside page (0x1150 -> 0x150)
 * @param [in] address_memory Address of memory
 * @return ::uint64_t
 */
static inline uint64_t PageOffset(uint64_t address_memory) {
  return (address_memory & kPageMask);
}

/**
 * @brief The function performs align address of memory to PAGE_SIZE
 * @param [in] address_memory Address of memory that need to align
 * @return ::uint64_t, Aligned address memory
 */
static inline uint64_t AlignToPageDown(uint64_t address_memory) {
  return PageAddress(address_memory);
}

/**
 * @brief The function performs align address of memory to PAGE_SIZE
 * @param [in] address_memory Address of memory that need to align
 * @return ::uint64_t, Aligned address memory
 */
static inline uint64_t AlignToPageUp(uint64_t address_memory) {
  return PageAddress(address_memory + kPageMask);
}

/**
 * Types of memory mapping access
 */
typedef enum {
  kMemoryAccessRead, //!< Operation is permitted only read
  kMemoryAccessWrite //!< Operation is permitted both read/write
} MemoryAccessType;

/**
 * @brief Check virtual address is valid in kernel space
 * @param [in]  kernel_virt Virtual address kernel space
 * @return ::1 it's kernel virtual address
 * ::0 it isn't kernel virtual address
 */
uint32_t IsKernelVirtualAddress(KernelAddress kernel_virt);

/**
 * @brief Check physical address is valid on the device
 * @param [in] phys Physical address
 * @return ::1 it's physical address
 * ::0 it isn't physical address
 */
uint32_t IsPhysicalAddress(PhysicalAddress phys);

/**
 * @brief Convert virtual address to physical address
 * @param [in]  virt Virtual address kernel space
 * @return ::PhysicalAddress Physical address
 */
PhysicalAddress KernelVirtToPhys(KernelAddress virt);

/**
 * @brief Get KASLR address
 * @return Physical address of KASLR structure
 */
PhysicalAddress KernelGetKaslrStructAddress(void);

/**
 * @brief Get physical address of pa config offset symbol
 * @return Physical address of sym_proca_conf_offset_lo32
 */
PhysicalAddress KernelGetPaConfigOffsetSymAddress(void);

/**
 * @brief Get value of PHYS_OFFSET kernel macro
 * @return Value of PHYS_OFFSET kernel macro
 */
PhysicalAddress KernelGetConfigPhysOffset(void);

/**
 * @brief Get value of address where kernel is loaded
 * @return Get value of address where kernel is loaded
 */
PhysicalAddress KernelGetStartPhysAddr(void);

/**
 * @brief Get physical address of _text symbol
 * @return Physical address of _text symbol
 */
PhysicalAddress KernelGetTextSymAddress(void);

/**
 * @brief Switch memory layout to another
 * @return ::PA_TZ_SUCCESS if another memory layout is present
 */
PaTzResult KernelNextMemoryConfiguration(void);

/**
 * @brief Convert virtual address (in Driver address space to physical address)
 * @param [in]  virt Virtual address
 * @param [out] phys Physical address
 * @warning The functions is supported only on T-Base now
 * @return ::PA_TZ_SUCCESS in case of success, ::PA_TZ_GENERAL_ERROR
 */
PaTzResult PlatformVirtToPhys64(const void *virt, PhysicalAddress *phys);

/**
 * @brief Platform depended function is mapped a physical address of memory
 * to virtual address
 * @param [in]  phys   Physical address some memory pages
 * @param [in]  size   Size of memory pages in bytes
 * @param [in]  type   Type of access for mapping operation (read/write etc)
 * @param [out] virt   Mapped virtual address
 * @return ::PA_TZ_SUCCESS in case of success, ::PA_TZ_GENERAL_ERROR
 */
PaTzResult PlatformSysMap(PhysicalAddress phys, size_t size,
                          MemoryAccessType type, void **virt);

/**
 * @brief Platform depended function is unmapped a virtual address
 * @param [in] virt   Virtual address some memory pages
 * @param [in] size   Size of memory pages in bytes
 * @return ::PA_TZ_SUCCESS in case of success, ::PA_TZ_GENERAL_ERROR
 */
PaTzResult PlatformSysUnmap(void *virt, size_t size);

/**
 * @brief Platform depended function is returned mapping flags
 * @param [in] type   Type of access for mapping operation (read/write etc)
 * @return ::uint32_t Mask for mapping operation
 */
uint32_t GetMappingFlags(MemoryAccessType type);

/**
 * @brief Map a physical address of region memory to virtual address
 * @param [in]  phys   Physical address some memory pages
 * @param [in]  size   Size of memory pages in bytes
 * @param [in]  type   Type of access for mapping operation (read/write etc)
 * @param [out] virt   Mapped virtual address
 * @return ::PA_TZ_SUCCESS in case of success, ::PA_TZ_GENERAL_ERROR
 */
PaTzResult PlatformMapRegion(PhysicalAddress phys, size_t size,
                             MemoryAccessType type, void **virt);

/**
 * @brief Unmap a region memory
 * @param [in] virt   Virtual address some memory pages
 * @param [in] size   Size of memory pages in bytes
 * @return ::PA_TZ_SUCCESS in case of success, ::PA_TZ_GENERAL_ERROR
 */
PaTzResult PlatformUnmapRegion(void *virt, size_t size);

/**
 * @brief Read number of bytes from physical address
 * @param [in] phys Physical address
 * @param [in] size Size of memory to output buffer
 * @param [out] out Pointer to output buffer
 * @return ::PA_TZ_SUCCESS in case of success, ::PA_TZ_GENERAL_ERROR
 */
PaTzResult PhysicalGetBytes(PhysicalAddress phys, size_t size, void *out);

/**
 * @brief Translate address from trustlet virtual address space to driver space
 * @param [in] virt_trustlet trustlet virtual address
 * @param [in] size Size of memory to translate
 * @param [in] type Translation type (read/write)
 * @param [out] virt_driver Address in driver virtual space
 * @return ::PA_TZ_SUCCESS in case of success, ::PA_TZ_GENERAL_ERROR
 */
PaTzResult PlatformSysMapTrustlet(const void *virt_trustlet, size_t size,
                                  MemoryAccessType type, void **virt_driver);

/**
 * @brief Unmap translated address
 * @param [in] virt_driver Driver virtual address
 * @param [in] size Size of memory to unmap
 * @return ::PA_TZ_SUCCESS in case of success, ::PA_TZ_GENERAL_ERROR
 */
PaTzResult PlatformSysUnmapTrustlet(const void *virt_driver, size_t size);

/**
 * @brief Read trusted OEM buffer
 * @param [in] offset Offset in OEM buffer in bytes
 * @param [in] size Size of read buffer
 * @param [out] out Buffer to output OEM data
 * @return ::PA_TZ_SUCCESS in case of success, ::PA_TZ_GENERAL_ERROR
 */
PaTzResult PlatformReadOemBuffer(uint32_t offset, size_t size, void *out);

/**
 * @brief Check memory region which begins from phys_addr with its region_size
 * is non secure or not
 * @param [in] phys_addr Physical address
 * @param [in] region_size Memory region size
 * @return ::PA_TZ_SUCCESS in case of success(memory region is non secure),
 *         ::PA_TZ_GENERAL_ERROR
 */
PaTzResult PlatformCheckNonSecureRegion(PhysicalAddress phys_addr, uint32_t region_size);

#endif  // PA_TZ_DRV_SRC_MEMORY_H_
