/*
 * Copyright (c) 2018 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @file
 * @brief ARM Core CMSE API
 *
 * CMSE API for Cortex-M23/M33 CPUs.
 */

#ifndef ZEPHYR_ARCH_ARM_INCLUDE_AARCH32_CORTEX_M_CMSE_H_
#define ZEPHYR_ARCH_ARM_INCLUDE_AARCH32_CORTEX_M_CMSE_H_

#ifdef _ASMLANGUAGE

/* nothing */

#else

#include <arm_cmse.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/*
 * Address information retrieval based on the TT instructions.
 *
 * The TT instructions are used to check the access permissions that different
 * security states and privilege levels have on memory at a specified address
 */

/**
 * @brief Get the MPU region number of an address
 *
 * Return the non-negative MPU region that the address maps to,
 * or -EINVAL to indicate that an invalid MPU region was retrieved.
 *
 * Note:
 * Obtained region is valid only if:
 * - the function is called from privileged mode
 * - the MPU is implemented and enabled
 * - the given address matches a single, enabled MPU region
 *
 * @param addr The address for which the MPU region is requested
 *
 * @return a valid MPU region number or -EINVAL
 */
int arm_cmse_mpu_region_get(uint32_t addr);

/**
 * @brief Read accessibility of an address
 *
 * Evaluates whether a specified memory location can be read according to the
 * permissions of the current state MPU and the specified operation mode.
 *
 * This function shall always return zero:
 * - if executed from an unprivileged mode,
 * - if the address matches multiple MPU regions.
 *
 * @param addr The address for which the readability is requested
 * @param force_npriv Instruct to return the readability of the address
 *                    for unprivileged access, regardless of whether the current
 *                    mode is privileged or unprivileged.
 *
 * @return 1 if address is readable, 0 otherwise.
 */
int arm_cmse_addr_read_ok(uint32_t addr, int force_npriv);

/**
 * @brief Read and Write accessibility of an address
 *
 * Evaluates whether a specified memory location can be read/written according
 * to the permissions of the current state MPU and the specified operation
 * mode.
 *
 * This function shall always return zero:
 * - if executed from an unprivileged mode,
 * - if the address matches multiple MPU regions.
 *
 * @param addr The address for which the RW ability is requested
 * @param force_npriv Instruct to return the RW ability of the address
 *                    for unprivileged access, regardless of whether the current
 *                    mode is privileged or unprivileged.
 *
 * @return 1 if address is Read and Writable, 0 otherwise.
 */
int arm_cmse_addr_readwrite_ok(uint32_t addr, int force_npriv);

/**
 * @brief Read accessibility of an address range
 *
 * Evaluates whether a memory address range, specified by its base address
 * and size, can be read according to the permissions of the current state MPU
 * and the specified operation mode.
 *
 * This function shall always return zero:
 * - if executed from an unprivileged mode,
 * - if the address range overlaps with multiple MPU (and/or SAU/IDAU) regions.
 *
 * @param addr The base address of an address range,
 *             for which the readability is requested
 * @param size The size of the address range
 * @param force_npriv Instruct to return the readability of the address range
 *                    for unprivileged access, regardless of whether the current
 *                    mode is privileged or unprivileged.
 *
 * @return 1 if address range is readable, 0 otherwise.
 */
int arm_cmse_addr_range_read_ok(uint32_t addr, uint32_t size, int force_npriv);

/**
 * @brief Read and Write accessibility of an address range
 *
 * Evaluates whether a memory address range, specified by its base address
 * and size, can be read/written according to the permissions of the current
 * state MPU and the specified operation mode.
 *
 * This function shall always return zero:
 * - if executed from an unprivileged mode,
 * - if the address range overlaps with multiple MPU (and/or SAU/IDAU) regions.
 *
 * @param addr The base address of an address range,
 *             for which the RW ability is requested
 * @param size The size of the address range
 * @param force_npriv Instruct to return the RW ability of the address range
 *                    for unprivileged access, regardless of whether the current
 *                    mode is privileged or unprivileged.
 *
 * @return 1 if address range is Read and Writable, 0 otherwise.
 */
int arm_cmse_addr_range_readwrite_ok(uint32_t addr, uint32_t size, int force_npriv);

/* Required for C99 compilation (required for GCC-8.x version,
 * where typeof is used instead of __typeof__)
 */
#ifndef typeof
#define typeof __typeof__
#endif

/**
 * @brief Read accessibility of an object
 *
 * Evaluates whether a given object can be read according to the
 * permissions of the current state MPU.
 *
 * The macro shall always evaluate to zero if called from an unprivileged mode.
 *
 * @param p_obj Pointer to the given object
 *              for which the readability is requested
 *
 * @pre Object is allocated in a single MPU (and/or SAU/IDAU) region.
 *
 * @return p_obj if object is readable, NULL otherwise.
 */
#define ARM_CMSE_OBJECT_READ_OK(p_obj) cmse_check_pointed_object(p_obj, CMSE_MPU_READ)

/**
 * @brief Read accessibility of an object (nPRIV mode)
 *
 * Evaluates whether a given object can be read according to the
 * permissions of the current state MPU (unprivileged read).
 *
 * The macro shall always evaluate to zero if called from an unprivileged mode.
 *
 * @param p_obj Pointer to the given object
 *              for which the readability is requested
 *
 * @pre Object is allocated in a single MPU (and/or SAU/IDAU) region.
 *
 * @return p_obj if object is readable, NULL otherwise.
 */
#define ARM_CMSE_OBJECT_UNPRIV_READ_OK(p_obj)                                                      \
	cmse_check_pointed_object(p_obj, CMSE_MPU_UNPRIV | CMSE_MPU_READ)

/**
 * @brief Read and Write accessibility of an object
 *
 * Evaluates whether a given object can be read and written
 * according to the permissions of the current state MPU.
 *
 * The macro shall always evaluate to zero if called from an unprivileged mode.
 *
 * @param p_obj Pointer to the given object
 *              for which the read and write ability is requested
 *
 * @pre Object is allocated in a single MPU (and/or SAU/IDAU) region.
 *
 * @return p_obj if object is Read and Writable, NULL otherwise.
 */
#define ARM_CMSE_OBJECT_READWRITE_OK(p_obj) cmse_check_pointed_object(p_obj, CMSE_MPU_READWRITE)

/**
 * @brief Read and Write accessibility of an object (nPRIV mode)
 *
 * Evaluates whether a given object can be read and written according
 * to the permissions of the current state MPU (unprivileged read/write).
 *
 * The macro shall always evaluate to zero if called from an unprivileged mode.
 *
 * @param p_obj Pointer to the given object
 *              for which the read and write ability is requested
 *
 * @pre Object is allocated in a single MPU (and/or SAU/IDAU) region.
 *
 * @return p_obj if object is Read and Writable, NULL otherwise.
 */
#define ARM_CMSE_OBJECT_UNPRIV_READWRITE_OK(p_obj)                                                 \
	cmse_check_pointed_object(p_obj, CMSE_MPU_UNPRIV | CMSE_MPU_READWRITE)

#if defined(CONFIG_ARM_SECURE_FIRMWARE)

/**
 * @brief Get the MPU (Non-Secure) region number of an address
 *
 * Return the non-negative MPU (Non-Secure) region that the address maps to,
 * or -EINVAL to indicate that an invalid MPU region was retrieved.
 *
 * Note:
 * Obtained region is valid only if:
 * - the function is called from Secure state
 * - the MPU is implemented and enabled
 * - the given address matches a single, enabled MPU region
 *
 * @param addr The address for which the MPU region is requested
 *
 * @return a valid MPU region number or -EINVAL
 */
int arm_cmse_mpu_nonsecure_region_get(uint32_t addr);

/**
 * @brief Get the SAU region number of an address
 *
 * Return the non-negative SAU (Non-Secure) region that the address maps to,
 * or -EINVAL to indicate that an invalid SAU region was retrieved.
 *
 * Note:
 * Obtained region is valid only if:
 * - the function is called from Secure state
 * - the SAU is implemented and enabled
 * - the given address is not exempt from the secure memory attribution
 *
 * @param addr The address for which the SAU region is requested
 *
 * @return a valid SAU region number or -EINVAL
 */
int arm_cmse_sau_region_get(uint32_t addr);

/**
 * @brief Get the IDAU region number of an address
 *
 * Return the non-negative IDAU (Non-Secure) region that the address maps to,
 * or -EINVAL to indicate that an invalid IDAU region was retrieved.
 *
 * Note:
 * Obtained region is valid only if:
 * - the function is called from Secure state
 * - the IDAU can provide a region number
 * - the given address is not exempt from the secure memory attribution
 *
 * @param addr The address for which the IDAU region is requested
 *
 * @return a valid IDAU region number or -EINVAL
 */
int arm_cmse_idau_region_get(uint32_t addr);

/**
 * @brief Security attribution of an address
 *
 * Evaluates whether a specified memory location belongs to a Secure region.
 * This function shall always return zero if executed from Non-Secure state.
 *
 * @param addr The address for which the security attribution is requested
 *
 * @return 1 if address is Secure, 0 otherwise.
 */
int arm_cmse_addr_is_secure(uint32_t addr);

/**
 * @brief Non-Secure Read accessibility of an address
 *
 * Evaluates whether a specified memory location can be read from Non-Secure
 * state according to the permissions of the Non-Secure state MPU and the
 * specified operation mode.
 *
 * This function shall always return zero:
 * - if executed from Non-Secure state
 * - if the address matches multiple MPU regions.
 *
 * @param addr The address for which the readability is requested
 * @param force_npriv Instruct to return the readability of the address
 *                    for unprivileged access, regardless of whether the current
 *                    mode is privileged or unprivileged.
 *
 * @return 1 if address is readable from Non-Secure state, 0 otherwise.
 */
int arm_cmse_addr_nonsecure_read_ok(uint32_t addr, int force_npriv);

/**
 * @brief Non-Secure Read and Write accessibility of an address
 *
 * Evaluates whether a specified memory location can be read/written from
 * Non-Secure state according to the permissions of the Non-Secure state MPU
 * and the specified operation mode.
 *
 * This function shall always return zero:
 * - if executed from Non-Secure  mode,
 * - if the address matches multiple MPU regions.
 *
 * @param addr The address for which the RW ability is requested
 * @param force_npriv Instruct to return the RW ability of the address
 *                    for unprivileged access, regardless of whether the current
 *                    mode is privileged or unprivileged.
 *
 * @return 1 if address is Read and Writable from Non-Secure state, 0 otherwise
 */
int arm_cmse_addr_nonsecure_readwrite_ok(uint32_t addr, int force_npriv);

/**
 * @brief Non-Secure Read accessibility of an address range
 *
 * Evaluates whether a memory address range, specified by its base address
 * and size, can be read according to the permissions of the Non-Secure state
 * MPU and the specified operation mode.
 *
 * This function shall always return zero:
 * - if executed from Non-Secure  mode,
 * - if the address matches multiple MPU (and/or SAU/IDAU) regions.
 *
 * @param addr The base address of an address range,
 *             for which the readability is requested
 * @param size The size of the address range
 * @param force_npriv Instruct to return the readability of the address range
 *                    for unprivileged access, regardless of whether the current
 *                    mode is privileged or unprivileged.
 *
 * @return 1 if address range is readable, 0 otherwise.
 */
int arm_cmse_addr_range_nonsecure_read_ok(uint32_t addr, uint32_t size, int force_npriv);

/**
 * @brief Non-Secure Read and Write accessibility of an address range
 *
 * Evaluates whether a memory address range, specified by its base address
 * and size, can be read and written according to the permissions of the
 * Non-Secure state MPU and the specified operation mode.
 *
 * This function shall always return zero:
 * - if executed from Non-Secure  mode,
 * - if the address matches multiple MPU (and/or SAU/IDAU) regions.
 *
 * @param addr The base address of an address range,
 *             for which Read and Write ability is requested
 * @param size The size of the address range
 * @param force_npriv Instruct to return the readability of the address range
 *                    for unprivileged access, regardless of whether the current
 *                    mode is privileged or unprivileged.
 *
 * @return 1 if address range is readable, 0 otherwise.
 */
int arm_cmse_addr_range_nonsecure_readwrite_ok(uint32_t addr, uint32_t size, int force_npriv);

/**
 * @brief Non-Secure Read accessibility of an object
 *
 * Evaluates whether a given object can be read according to the
 * permissions of the Non-Secure state MPU.
 *
 * The macro shall always evaluate to zero if called from Non-Secure state.
 *
 * @param p_obj Pointer to the given object
 *              for which the readability is requested
 *
 * @pre Object is allocated in a single MPU region.
 *
 * @return p_obj if object is readable from Non-Secure state, NULL otherwise.
 */
#define ARM_CMSE_OBJECT_NONSECURE_READ_OK(p_obj)                                                   \
	cmse_check_pointed_object(p_obj, CMSE_NONSECURE | CMSE_MPU_READ)

/**
 * @brief Non-Secure Read accessibility of an object (nPRIV mode)
 *
 * Evaluates whether a given object can be read according to the
 * permissions of the Non-Secure state MPU (unprivileged read).
 *
 * The macro shall always evaluate to zero if called from Non-Secure state.
 *
 * @param p_obj Pointer to the given object
 *              for which the readability is requested
 *
 * @pre Object is allocated in a single MPU region.
 *
 * @return p_obj if object is readable from Non-Secure state, NULL otherwise.
 */
#define ARM_CMSE_OBJECT_NONSECURE_UNPRIV_READ_OK(p_obj)                                            \
	cmse_check_pointed_object(p_obj, CMSE_NONSECURE | CMSE_MPU_UNPRIV | CMSE_MPU_READ)

/**
 * @brief Non-Secure Read and Write accessibility of an object
 *
 * Evaluates whether a given object can be read and written
 * according to the permissions of the Non-Secure state MPU.
 *
 * The macro shall always evaluate to zero if called from Non-Secure state.
 *
 * @param p_obj Pointer to the given object
 *              for which the read and write ability is requested
 *
 * @pre Object is allocated in a single MPU region.
 *
 * @return p_obj if object is Non-Secure Read and Writable, NULL otherwise.
 */
#define ARM_CMSE_OBJECT_NONSECURE_READWRITE_OK(p_obj)                                              \
	cmse_check_pointed_object(p_obj, CMSE_NONSECURE | CMSE_MPU_READWRITE)

/**
 * @brief Non-Secure Read and Write accessibility of an object (nPRIV mode)
 *
 * Evaluates whether a given object can be read and written according
 * to the permissions of the Non-Secure state MPU (unprivileged read/write).
 *
 * The macro shall always evaluate to zero if called from Non-Secure state.
 *
 * @param p_obj Pointer to the given object
 *              for which the read and write ability is requested
 *
 * @pre Object is allocated in a single MPU region.
 *
 * @return p_obj if object is Non-Secure Read and Writable, NULL otherwise.
 */
#define ARM_CMSE_OBJECT_NON_SECURE_UNPRIV_READWRITE_OK(p_obj)                                      \
	cmse_check_pointed_object(p_obj, CMSE_NONSECURE | CMSE_MPU_UNPRIV | CMSE_MPU_READWRITE)

#endif /* CONFIG_ARM_SECURE_FIRMWARE */

#ifdef __cplusplus
}
#endif

#endif /* _ASMLANGUAGE */

#endif /* ZEPHYR_ARCH_ARM_INCLUDE_AARCH32_CORTEX_M_CMSE_H_ */
