/*
 *  COPYRIGHT (c) 2012 Chris Johns <chrisj@rtems.org>
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.org/license/LICENSE.
 */
/**
 * @file
 *
 * @ingroup rtems_rtl
 *
 * @brief RTEMS Run-Time Linker Allocator
 */

#if !defined (_RTEMS_RTL_ALLOCATOR_H_)
#define _RTEMS_RTL_ALLOCATOR_H_

#include <stdbool.h>

#include "rtl-indirect-ptr.h"

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

/**
 * Define the types of allocation the loader requires.
 *
 * @note It is best to use the object tag for general memory allocation and to
 *       leave the tags with specific access properties to the module data
 */
enum rtems_rtl_alloc_tags_e {
  RTEMS_RTL_ALLOC_OBJECT,     /**< A generic memory object. */
  RTEMS_RTL_ALLOC_SYMBOL,     /**< Memory used for symbols. */
  RTEMS_RTL_ALLOC_EXTERNAL,   /**< Memory used for external symbols. */
  RTEMS_RTL_ALLOC_READ,       /**< The memory is read only. */
  RTEMS_RTL_ALLOC_READ_WRITE, /**< The memory is read and write. */
  RTEMS_RTL_ALLOC_READ_EXEC   /**< The memory is read and executable. */
};

/**
 * The allocator tag type.
 */
typedef enum rtems_rtl_alloc_tags_e rtems_rtl_alloc_tag_t;

/**
 * The number of tags.
 */
#define RTEMS_RTL_ALLOC_TAGS ((size_t) (RTEMS_RTL_ALLOC_READ_EXEC + 1))

/**
 * Allocator handler handles all RTL allocations. It can be hooked and
 * overridded for customised allocation schemes or memory maps.
 *
 * @param allocation If true the request is to allocate memory else free.
 * @param tag The type of allocation request.
 * @param address Pointer to the memory address. If an allocation the value is
 *                unspecific on entry and the allocated address or NULL on
 *                exit. The NULL value means the allocation failed. If a delete
 *                or free request the memory address is the block to free. A
 *                free request of NULL is silently ignored.
 * @param size The size of the allocation if an allocation request and
 *             not used if deleting or freeing a previous allocation.
 */
typedef void (*rtems_rtl_allocator_t)(bool                  allocate,
                                      rtems_rtl_alloc_tag_t tag,
                                      void**                address,
                                      size_t                size);

/**
 * The allocator data.
 */
struct rtems_rtl_alloc_data_s {
  /**< The memory allocator handler. */
  rtems_rtl_allocator_t allocator;
  /**< The indirect pointer chains. */
  rtems_chain_control indirects[RTEMS_RTL_ALLOC_TAGS];
};

typedef struct rtems_rtl_alloc_data_s rtems_rtl_alloc_data_t;

/**
 * Initialise the allocate data.
 *
 * @param data The data to initialise.
 */
void rtems_rtl_alloc_initialise (rtems_rtl_alloc_data_t* data);

/**
 * The Runtime Loader allocator new allocates new memory and optionally clear
 * the memory if requested.
 *
 * @param tag The type of allocation request.
 * @param size The size of the allocation.
 * @param zero If true the memory is cleared.
 * @return void* The memory address or NULL is not memory available.
 */
void* rtems_rtl_alloc_new (rtems_rtl_alloc_tag_t tag, size_t size, bool zero);

/**
 * The Runtime Loader allocator delete deletes allocated memory.
 *
 * @param tag The type of allocation request.
 * @param address The memory address to delete. A NULL is ignored.
 */
void rtems_rtl_alloc_del (rtems_rtl_alloc_tag_t tag, void* address);

/**
 * Hook the Runtime Loader allocatior. A handler can call the previous handler
 * in the chain to use it for specific tags. The default handler uses the
 * system heap. Do not unhook your handler if memory it allocates has not been
 * returned.
 *
 * @param handler The handler to use as the allocator.
 * @return rtems_rtl_alloc_handler_t The previous handler.
 */
rtems_rtl_allocator_t rtems_rtl_alloc_hook (rtems_rtl_allocator_t handler);

/**
 * Allocate memory to an indirect handle.
 *
 * @param tag The type of allocation request.
 * @param handle The handle to allocate the memory to.
 * @param size The size of the allocation.
 */
void rtems_rtl_alloc_indirect_new (rtems_rtl_alloc_tag_t tag,
                                   rtems_rtl_ptr_t*      handle,
                                   size_t                size);

/**
 * Free memory from an indirect handle.
 *
 * @param tag The type of allocation request.
 * @param handle The handle to free the memory from.
 */
void rtems_rtl_alloc_indirect_del (rtems_rtl_alloc_tag_t tag,
                                   rtems_rtl_ptr_t*      handle);

/**
 * Allocate the memory for a module given the size of the text, const, data and
 * bss sections. If any part of the allocation fails the no memory is
 * allocated.
 *
 * @param text_base Pointer to the text base pointer.
 * @param text_size The size of the read/exec section.
 * @param const_base Pointer to the const base pointer.
 * @param const_size The size of the read only section.
 * @param eh_base Pointer to the eh base pointer.
 * @param eh_size The size of the eh section.
 * @param data_base Pointer to the data base pointer.
 * @param data_size The size of the read/write secton.
 * @param bss_base Pointer to the bss base pointer.
 * @param bss_size The size of the read/write.
 * @retval true The memory has been allocated.
 * @retval false The allocation of memory has failed.
 */
bool rtems_rtl_alloc_module_new (void** text_base, size_t text_size,
                                 void** const_base, size_t const_size,
                                 void** eh_base, size_t eh_size,
                                 void** data_base, size_t data_size,
                                 void** bss_base, size_t bss_size);

/**
 * Free the memory allocated to a module.
 *
 * @param text_base Pointer to the text base pointer.
 * @param const_base Pointer to the const base pointer.
 * @param eh_base Pointer to the eh base pointer.
 * @param data_base Pointer to the data base pointer.
 * @param bss_base Pointer to the bss base pointer.
 */
void rtems_rtl_alloc_module_del (void** text_base, void** const_base,
                                 void** eh_base, void** data_base,
                                 void** bss_base);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif
