/**
 * @file    scp_rand.h
 * @brief   SCP features for random-number generation.
 *
 * @note    Client applications should include scp.h instead of this file.
 *
 * Copyright (c) 2021-2022, NVIDIA CORPORATION. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * * Neither the name of NVIDIA CORPORATION nor the names of its
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef SCP__SCP_RAND_H
#define SCP__SCP_RAND_H
#define FSP__SCP__SCP_RAND_H 1

/* Compiler headers */
#include <stddef.h>                     // for size_t
#include <stdint.h>                     // for uintptr_t

/* Early FSP headers */
#include <misc/ct-assert.h>             // for HEADER_CHECK

/* Hardware headers */

/* Late FSP headers */
#include <error/common-errors.h>        // for error_t

/* Module-specific FSP headers */

/*
 * Compile-time check for FSP header files
 *   Each FSP header file contains a signature unique to that file, and the
 *   FSP project.  The HEADER_CHECK macro (contained in misc/ct-assert.h) can
 *   check for this signature.  If it does not exist, then the build will
 *   abort.
 *
 *   This is a trap for projects which have their own include files of the
 *   same names, but different contents.  This trap ensures that only the
 *   files from the FSP project, are built into the FSP source code.
 */
HEADER_CHECK(FSP__ERROR__COMMON_ERRORS_H)


/**
 * @brief The RNG block size.
 *
 * The size, in bytes, of a single block of data in the context of RNG
 * operations.
 */
#define SCP_RAND_SIZE 16U


/**
 * @brief A collection of supported configurations for the SCP RNG hardware.
 */
typedef enum scp_rand_config_t
{
    /**
     * @brief The default configuration recommended by hardware for optimal RNG.
     */
    SCP_RAND_CONFIG_DEFAULT,

    /**
     * @brief A configuration that generates fake, deterministic data.
     *
     * Intended for testing and debugging purposes. Note that this configuration
     * is only supported when the SCP unit is in debug mode and also requires
     * that fake-RNG support be enabled during build.
     */
    SCP_RAND_CONFIG_FAKE,

} scp_rand_config_t;


/**
 * @brief Configures the SCP RNG hardware for use.
 *
 * Configures the SCP RNG hardware according to the value of config.
 *
 * Applications must call scp_Configure_Rand() before attempting to start the
 * RNG hardware and must stop the hardware again before attempting to
 * reconfigure, should the need arise.
 *
 * @param[in]  config           The hardware configuration to use. See the
 *                              documentation for scp_rand_config_t for a list
 *                              of supported values.
 *
 * @retval E_SUCCESS                    if configuration settings were applied
 *                                      successfully.
 *
 * @retval E_SCP_NOT_READY              if the SCP driver has not been
 *                                      initialized.
 *
 * @retval E_BUSY                       if the RNG hardware is currently running
 *                                      and cannot be reconfigured.
 *
 * @retval E_INVALID_PARAM              if config is not a valid
 *                                      scp_rand_config_t value.
 *
 * @retval E_SCP_INVALID_REQUEST        if config is SCP_RAND_CONFIG_FAKE but
 *                                      the SCP unit is not in debug mode.
 *
 * @retval E_SCP_FEATURE_NOT_ENABLED    if config is SCP_RAND_CONFIG_FAKE but
 *                                      the application does not have fake-RNG
 *                                      support enabled.
 *
 * @retval E_INVALID_STATE              if calls to this function are prohibited
 *                                      because the SCP driver is operating in
 *                                      direct mode.
 *
 * @retval E_SCP_INVALID_LOCK_STATE     if access to certain necessary registers
 *                                      is being blocked by SCP lockdown.
 */
error_t scp_Configure_Rand(scp_rand_config_t config);

/**
 * @brief Starts the SCP RNG hardware.
 *
 * Starts the SCP unit's RNG hardware. Applications must call scp_Start_Rand()
 * before attempting to acquire random data from scp_Get_Rand() (or scp_rand()).
 *
 * Note that there is some delay between starting the RNG hardware and
 * acquiring the first RNG data from it. The length of this delay is dictated
 * by the specific hardware configuration set by scp_Configure_Rand() and
 * manifests as increased latency in the execution of scp_Get_Rand().
 *
 * This function is safe to call from direct mode.
 *
 * @retval E_SUCCESS                if RNG hardware was started successfully.
 *
 * @retval E_SCP_NOT_READY          if the SCP driver has not been initialized.
 *
 * @retval E_INVALID_STATE          if the application has not called
 *                                  scp_Configure_Rand() to configure the RNG
 *                                  hardware for use.
 *
 * @retval E_SCP_INVALID_LOCK_STATE if access to certain necessary registers is
 *                                  being blocked by SCP lockdown.
 *
 * @retval E_SCP_NOTHING_TO_DO      if the RNG hardware is already running (no-
 *                                  op).
 */
error_t scp_Start_Rand(void);

/**
 * @brief Stops the SCP RNG hardware.
 *
 * Stops the SCP unit's RNG hardware. Applications must call scp_Stop_Rand()
 * before attempting to reconfigure the RNG hardware via scp_Configure_Rand().
 *
 * This function is safe to call from direct mode.
 *
 * @retval E_SUCCESS                if RNG hardware was stopped successfully.
 *
 * @retval E_SCP_NOT_READY          if the SCP driver has not been initialized.
 *
 * @retval E_SCP_INVALID_LOCK_STATE if access to certain necessary registers is
 *                                  being blocked by SCP lockdown.
 *
 * @retval E_SCP_NOTHING_TO_DO      if the RNG hardware is already stopped (no-
 *                                  op).
 */
error_t scp_Stop_Rand(void);

/**
 * @brief Generates an arbitrary string of true-random data.
 *
 * Generates size bytes of true-random data and writes the result to dest_pa.
 * Uses the first SCP_RAND_SIZE bytes from context_pa as a contextual input to
 * the generation process, if provided.
 *
 * Applications must call scp_Start_Rand() to ensure that the RNG hardware is
 * running before attempting to acquire data from scp_Get_Rand().
 *
 * Keep in mind that the quality of the data generated by scp_Get_Rand() is
 * heavily influenced by the RNG hardware's specific configuration settings,
 * which also affect the rate at which the random data is produced. These
 * settings can be tuned via scp_Configure_Rand().
 *
 * In any case, the data produced by scp_Get_Rand() is not NIST-compliant and
 * should not be used to generate encryption keys or initialization vectors.
 *
 * Note that it is permissible for dest_pa and context_pa to alias to the same
 * memory location.
 *
 * In the event that dest_pa refers to an external memory location, the
 * application must ensure that it has first called scp_Configure_Shortcut() (or
 * used the SCP_EXTERNAL_DEST() helper macro) to configure the shortcut-DMA
 * path accordingly.
 *
 * @param[out] dest_pa      The physical address of the output buffer to which
 *                          the generated data is to be written. Must be
 *                          aligned to SCP_BUFFER_ALIGNMENT and have at least
 *                          size bytes of capacity. Supports IMEM, DMEM, and
 *                          external locations.
 *
 * @param[in]  size         The number of bytes of data to generate. Must be a
 *                          nonzero multiple of SCP_RAND_SIZE.
 *
 * @param[in]  context_pa   The physical address of the input buffer containing
 *                          the initial context value to be factored into the
 *                          generated data (zero/NULL if none). Must be aligned
 *                          to SCP_BUFFER_ALIGNMENT and contain at least
 *                          SCP_RAND_SIZE bytes. Permitted to alias to dest_pa.
 *                          Supports IMEM and DMEM locations only.
 *
 * @retval E_SUCCESS                    if data was generated successfully.
 *
 * @retval E_SCP_NOT_READY              if the SCP driver has not been
 *                                      initialized.
 *
 * @retval E_SCP_INVALID_OPERATION      if the RNG hardware is not running or
 *                                      has not been properly configured.
 *
 * @retval E_SCP_INVALID_POINTER        if dest_pa is zero/NULL.
 *
 * @retval E_SCP_INVALID_ADDRESS        if one or more of dest_pa or context_pa
 *                                      is incorrectly aligned.
 *
 * @retval E_SCP_INVALID_BASE           if one or more of dest_pa or context_pa
 *                                      points to an unsupported memory region
 *                                      (e.g. EMEM).
 *
 * @retval E_SCP_INVALID_REQUEST        if size is zero or is not a multiple of
 *                                      SCP_RAND_SIZE.
 *
 * @retval E_SCP_FEATURE_NOT_ENABLED    if dest_pa is located in external memory
 *                                      but the application does not have
 *                                      shortcut-DMA support enabled.
 *
 * @retval E_SCP_INVALID_DMA_SPECIFIER  if dest_pa is located in external memory
 *                                      but the application has not called
 *                                      scp_Configure_Shortcut() to configure
 *                                      the shortcut-DMA path.
 *
 * @retval E_SCP_OUT_OF_RANGE           if one or more of the buffers referenced
 *                                      by dest_pa or context_pa overflows the
 *                                      memory region in which it resides.
 *
 * @retval E_SCP_INSUFFICIENT_RESOURCES if the application lacks access to
 *                                      secret-index zero (required in secure
 *                                      contexts).
 *
 * @retval E_INVALID_STATE              if calls to this function are prohibited
 *                                      because the SCP driver is operating in
 *                                      direct mode.
 *
 * @retval E_SCP_INVALID_LOCK_STATE     if access to certain necessary registers
 *                                      is being blocked by SCP lockdown.
 *
 * @retval E_FAULT                      if an IO-PMP fault was detected.
 *
 * @retval E_SCP_GENERIC                if an unexpected error occurred.
 */
error_t scp_Get_Rand(uintptr_t dest_pa, size_t size, uintptr_t context_pa);

#endif // SCP__SCP_RAND_H
