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

/**
 * @file
 * @brief TrustZone API for use in nonsecure firmware
 *
 * TrustZone API for Cortex-M CPUs implementing the Security Extension.
 * The following API can be used by the nonsecure firmware to interact with the
 * secure firmware.
 */

#ifndef ZEPHYR_ARCH_ARM_INCLUDE_AARCH32_CORTEX_M_TZ_NS_H_
#define ZEPHYR_ARCH_ARM_INCLUDE_AARCH32_CORTEX_M_TZ_NS_H_

#ifdef _ASMLANGUAGE

/* nothing */

#else

/**
 * @brief Macro for "sandwiching" a function call (@p name) between two other
 *	  calls
 *
 * This macro should be called via @ref __TZ_WRAP_FUNC.
 *
 * This macro creates the function body of an "outer" function which behaves
 * exactly like the wrapped function (@p name), except that the preface function
 * is called before, and the postface function afterwards.
 *
 * @param preface   The function to call first. Must have no parameters and no
 *                  return value.
 * @param name      The main function, i.e. the function to wrap. This function
 *                  will receive the arguments, and its return value will be
 *                  returned.
 * @param postface  The function to call last. Must have no parameters and no
 *                  return value.
 * @param store_lr  The assembly instruction for storing away the LR value
 *                  before the functions are called. This instruction must leave
 *                  r0-r3 unmodified.
 * @param load_lr   The assembly instruction for restoring the LR value after
 *                  the functions have been called. This instruction must leave
 *                  r0-r3 unmodified.
 */
#define __TZ_WRAP_FUNC_RAW(preface, name, postface, store_lr, load_lr)                             \
	__asm__ volatile(".global " #preface "; .type " #preface ", %function");                   \
	__asm__ volatile(".global " #name "; .type " #name ", %function");                         \
	__asm__ volatile(".global " #postface "; .type " #postface ", %function");                 \
	__asm__ volatile(store_lr "\n\t"                                                           \
				  "push {r0-r3}\n\t"                                               \
				  "bl " #preface "\n\t"                                            \
				  "pop {r0-r3}\n\t"                                                \
				  "bl " #name "\n\t"                                               \
				  "push {r0-r3}\n\t"                                               \
				  "bl " #postface "\n\t"                                           \
				  "pop {r0-r3}\n\t" load_lr "\n\t" ::);

/**
 * @brief Macro for "sandwiching" a function call (@p name) in two other calls
 *
 * @pre The wrapped function MUST not pass arguments or return values via
 *      the stack. I.e. the arguments and return values must each fit within 4
 *      words, after accounting for alignment.
 *      Since nothing is passed on the stack, the stack can safely be used to
 *      store LR.
 *
 * Usage example:
 *
 *	int foo(char *arg); // Implemented elsewhere.
 *	int __attribute__((naked)) foo_wrapped(char *arg)
 *	{
 *		__TZ_WRAP_FUNC(bar, foo, baz)
 *	}
 *
 * is equivalent to
 *
 *	int foo(char *arg); // Implemented elsewhere.
 *	int foo_wrapped(char *arg)
 *	{
 *		bar();
 *		int res = foo(arg);
 *		baz();
 *		return res;
 *	}
 *
 * @note __attribute__((naked)) is not mandatory, but without it, GCC gives a
 *       warning for functions with a return value. It also reduces flash use.
 *
 * See @ref __TZ_WRAP_FUNC_RAW for more information.
 */
#define __TZ_WRAP_FUNC(preface, name, postface)                                                    \
	__TZ_WRAP_FUNC_RAW(preface, name, postface, "push {r4, lr}", "pop {r4, pc}")

#ifdef CONFIG_ARM_FIRMWARE_USES_SECURE_ENTRY_FUNCS
/**
 * @brief Create a thread safe wrapper function for a non-secure entry function
 *
 * This locks the scheduler before calling the function by wrapping the NS entry
 * function in @ref k_sched_lock / @ref k_sched_unlock, using
 * @ref __TZ_WRAP_FUNC.
 *
 * In non-secure code:
 *
 *	int foo(char *arg); // Declaration of entry function.
 *	TZ_THREAD_SAFE_NONSECURE_ENTRY_FUNC(foo_safe, int, foo, char *arg)
 *
 * Usage in non-secure code:
 *
 *	int ret = foo_safe("my arg");
 *
 * If NS entry functions are called without such a wrapper, and a thread switch
 * happens while execution is in the secure binary, the app will possibly crash
 * upon returning to the non-secure binary.
 *
 * @param ret   The return type of the NS entry function.
 * @param name  The desired name of the safe function. This assumes there is a
 *              corresponding NS entry function called nsc_name.
 * @param ...   The rest of the signature of the function. This must be the same
 *              signature as the corresponding NS entry function.
 */
#define TZ_THREAD_SAFE_NONSECURE_ENTRY_FUNC(name, ret, nsc_name, ...)                              \
	ret __attribute__((naked)) name(__VA_ARGS__)                                               \
	{                                                                                          \
		__TZ_WRAP_FUNC(k_sched_lock, nsc_name, k_sched_unlock);                            \
	}

#endif /* CONFIG_ARM_FIRMWARE_USES_SECURE_ENTRY_FUNCS */

#endif /* _ASMLANGUAGE */
#endif /* ZEPHYR_ARCH_ARM_INCLUDE_AARCH32_CORTEX_M_TZ_NS_H_ */
