/*
 * almosmkh.h - User level ALMOS-MKH specific library definition.
 * 
 * Author     Alain Greiner (2016,2017,2018)
 *
 * Copyright (c) UPMC Sorbonne Universites
 *
 * This file is part of ALMOS-MKH.
 *
 * ALMOS-MKH is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2.0 of the License.
 *
 * ALMOS-MKH is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef _LIBALMOSMKH_H_
#define _LIBALMOSMKH_H_

/*************************************************************************************** 
 * This file defines an user level, ALMOS-MKH specific library, containing: 
 * - non standard system calls.
 * - debug functions.
 * - remote malloc extensions. 
 **************************************************************************************/

#include <pthread.h>
#include <shared_almos.h>

/****************** Non standard (ALMOS_MKH specific) system calls ********************/


/*************************************************************************************** 
 * This syscall gives the process identified by the <pid> argument the exclusive 
 * ownership of its TXT terminal.
 *************************************************************************************** 
 * @ pid        : process identifier.
 * @ returns O if success / returns -1 if process not found.
 **************************************************************************************/
int fg( unsigned int pid );

/*************************************************************************************** 
 * This syscall stores in the buffer identified by the <owner> argument a non zero
 * value when the process identified by the <pid> argument is currently the exclusive
 * owner of its TXT terminal.
 *************************************************************************************** 
 * @ pid        : [in]  process identifier.
 * @ owner      : [out] pointer on buffer to store the
 * @ returns O if success / returns -1 if process not found.
 **************************************************************************************/
int is_fg( unsigned int pid,
           unsigned int * owner );

/*************************************************************************************** 
 * This syscall returns the hardware platform parameters.
 ***************************************************************************************
 * @ x_size   : [out] number of clusters in a row.
 * @ y_size   : [out] number of clusters in a column.
 * @ ncores   : [out] number of cores per cluster.
 * @ return always 0.
 **************************************************************************************/
int get_config( unsigned int * x_size,
                unsigned int * y_size,
                unsigned int * ncores );

/*************************************************************************************** 
 * This syscall returns the cluster an local index for the calling core.
 ***************************************************************************************
 * @ cxy      : [out] cluster identifier.
 * @ lid      : [out] core local index in cluster.
 * @ return always 0.
 **************************************************************************************/
int get_core( unsigned int * cxy,
              unsigned int * lid );

/***************************************************************************************
 * This function returns the calling core cycles counter,
 * taking into account a possible overflow on 32 bits architectures.
 ***************************************************************************************
 * @ cycle    : [out] current cycle value.
 * @ return always 0.
 **************************************************************************************/
int get_cycle( unsigned long long * cycle );

/***************************************************************************************
 * This syscall allows the calling thread to specify the target cluster for
 * a subsequent fork(). It must be called for each fork().
 ***************************************************************************************
 * @ cxy      : [in] target cluster identifier.
 * @ return 0 if success / returns -1 if illegal cxy argument.
 **************************************************************************************/
int place_fork( unsigned int cxy );

/*************************************************************************************** 
 * This syscall implements the operations related to User Thread Local Storage.
 ***************************************************************************************
 * @ operation  : UTLS operation type as defined in "shared_sycalls.h" file.
 * @ value      : argument value for the UTLS_SET operation.
 * @ return value for the UTLS_GET and UTLS_GET_ERRNO / return -1 if failure.
 **************************************************************************************/
int utls( unsigned int operation,
          unsigned int value );

/***************************************************************************************
 * This syscall returns an unsigned 32 bits integer from the standard "stdin" stream.
 * Both decimal numbers and hexadecimal numbers (prefixed by 0x) are supported.
 ***************************************************************************************
 * returns the integer value if success / returns -1 if failure.
 **************************************************************************************/
unsigned int get_uint32( void );


/***************** Non standard (ALMOS-MKH specific) debug functions ******************/


/*************************************************************************************** 
 * This debug syscall displays on the kernel terminal TXT0
 * the thread / process / core identifiers, the current cycle, plus a user defined
 * message as specified by the <string> argument. 
 ***************************************************************************************
 * @ string    : [in] user defined message.
 **************************************************************************************/
void display_string( char * string );

/*************************************************************************************** 
 * This debug function displays on the kernel terminal TXT0 
 * the state of the  VMM for the process <pid>, in cluster <cxy>.
 * It can be called by any thread running in any cluster.
 ***************************************************************************************
 * @ pid      : [in] process identifier.
 * @ return 0 if success / return -1 if illegal argument.
 **************************************************************************************/
int display_vmm(unsigned int cxy, unsigned int pid );

/*************************************************************************************** 
 * This debug syscall displays on the kernel terminal TXT0 
 * the state of the core scheduler identified by the <cxy> and <lid> arguments.
 * It can be called by any thread running in any cluster.
 ***************************************************************************************
 * @ cxy      : [in] target cluster identifier.
 * @ lid      : [in] target core local index.
 * @ return 0 if success / return -1 if illegal arguments.
 **************************************************************************************/
int display_sched( unsigned int  cxy,
                   unsigned int  lid );

/*************************************************************************************** 
 * This debug syscall displays on the kernel terminal TXT0 
 * the list of process registered in a given cluster identified by the <cxy> argument.
 * Only the owned processes are displayed when the <owned> argument is non zero.
 * It can be called by any thread running in any cluster.
 ***************************************************************************************
 * @ cxy      : [in] target cluster identifier.
 * @ owned    : [in] only owned processes if non zero.
 * @ return 0 if success / return -1 if illegal argument.
 **************************************************************************************/
int display_cluster_processes( unsigned int  cxy,
                               unsigned int  owned );

/*************************************************************************************** 
 * This debug syscall displays on the kernel terminal TXT0 
 * the list of processes attached to a given TXT channel.
 * It can be called by any thread running in any cluster.
 ***************************************************************************************
 * @ txt_id   : [in] TXT terminal indes.
 * @ return 0 if success / return -1 if illegal argument.
 **************************************************************************************/
int display_txt_processes( unsigned int txt_id );

/*************************************************************************************** 
 * This debug syscall displays on the kernel terminal TXT0 
 * the set of busylocks hold by a thread identified by the <pid> and <trdid> arguments.
 * It can be called by any thread running in any cluster.
 ***************************************************************************************
 * @ pid      : [in] process identifier.
 * @ trdid    : [in] thread identifier.
 * @ return 0 if success / return -1 if illegal arguments.
 **************************************************************************************/
int display_busylocks( unsigned int pid,
                       unsigned int trdid );

/*************************************************************************************** 
 * This debug syscall displays on the kernel terminal TXT0 
 * the list of channel devices available in the architecture.
 * It can be called by any thread running in any cluster.
 ***************************************************************************************
 * @ return always 0.
 **************************************************************************************/
int display_chdev( void );

/*************************************************************************************** 
 * This debug syscall displays on the kernel terminal TXT0 
 * the list of channel device or pseudo-files registered in the VFS cache.
 * It can be called by any thread running in any cluster.
 ***************************************************************************************
 * @ return always 0.
 **************************************************************************************/
int display_vfs( void );

/*************************************************************************************** 
 * This debug syscall displays on the kernel terminal TXT0 the current DQDT state.
 * It can be called by any thread running in any cluster.
 ***************************************************************************************
 * @ return always 0.
 **************************************************************************************/
int display_dqdt( void );

/***************************************************************************************** 
* This debug syscall is used to activate / desactivate the context switches trace
* for a core identified by the <cxy> and <lid> arguments.
* It can be called by any thread running in any cluster.
***************************************************************************************** 
* @ active     : activate trace if non zero / desactivate if zero.
* @ cxy        : cluster identifier.
* @ lid        : core local index.
* @ returns O if success / returns -1 if illegal arguments.
****************************************************************************************/
int trace( unsigned int active,
           unsigned int cxy, 
           unsigned int lid );

/****************************************************************************************
 * This syscall implements an user-level interactive debugger that can be
 * introduced in any user application to display various kernel distributed structures.
 * The supported commands are:
 * - p (cxy)     : display all processes descriptors in a given cluster.
 * - s (cxy,lid) : display all threads attached to a given core in a given cluster.
 * - v (cxy)     : display the calling process VMM in a given cluster.
 * - t (tid)     : display all owner process descriptors attached to a TXT terminal.
 * - x           : force the calling process to exit.
 * - c           : continue calling process execution.
 * - h           : list the supported commands
 ***************************************************************************************/
void idbg( void );


/****************** Non standard (ALMOS-MKH specific) malloc operations  ***************/

/////////////////////////////////////////////////////////////////////////////////////////
// General principles:
// - In user space the HEAP zone spread between the ELF zone and the STACK zone,
//   as defined in the kernel_config.h file.
// - The malloc library uses the mmap() syscall to create - on demand -
//   one vseg in a given cluster. The size of this vseg is defined below
//   by the MALLOC_LOCAL_STORE_SIZE parameter.
// - For a standard malloc(), the target cluster is the cluster containing
//   the core running the client thread.
// - For a remote_malloc(), the target cluster is explicitely defined
//   by the argument.
// - In each cluster, the available storage in virtual space is handled by a
//   local allocator using the buddy algorithm.
//
// TODO : In this first implementation one single - fixed size - vseg 
//        is allocated on demand in each cluster.
//        We should introduce the possibility to dynamically allocate
//        several vsegs in each cluster, using several mmap when required. 
/////////////////////////////////////////////////////////////////////////////////////////
// Free blocks organisation in each cluster :
// - All free blocks have a size that is a power of 2, larger or equal
//   to MALLOC_MIN_BLOCK_SIZE (typically 64 bytes).
// - All free blocks are aligned.
// - They are pre-classed in an array of linked lists, where all blocks in a
//   given list have the same size. 
// - The NEXT pointer implementing those linked lists is written 
//   in the first bytes of the block itself, using the unsigned int type.
// - The pointers on the first free block for each size are stored in an
//   array of pointers free[32] in the storage(x,y) descriptor.
/////////////////////////////////////////////////////////////////////////////////////////
// Allocation policy:
// - The block size required by the user can be any value, but the allocated
//   block size can be larger than the requested size:
// - The allocator computes actual_size, that is the smallest power of 2 
//   value larger or equal to the requested size AND larger or equal to
//   MALLOC_MIN_BLOCK_SIZE.
// - It pop the linked list of free blocks corresponding to actual_size,
//   and returns the block B if the list[actual_size] is not empty.
// - If the list[actual_size] is empty, it pop the list[actual_size * 2].
//   If a block B' is found, it breaks this block in 2 B/2 blocks, returns 
//   the first B/2 block and push the other B/2 block into list[actual_size].
// - If the list[actual_size * 2] is empty, it pop the list[actual_size * 4].
//   If a block B is found, it break this block in 3 blocks B/4, B/4 and B/2,
//   returns the first B/4 block, push the other blocks B/4 and B/2 into
//   the proper lists. etc... 
// - If no block satisfying the request is available it returns a failure
//   (NULL pointer).
// - This allocation policy has the nice following property:
//   If the vseg is aligned (the vseg base is a multiple of the
//   vseg size), all allocated blocks are aligned on the actual_size.
/////////////////////////////////////////////////////////////////////////////////////////
// Free policy:
// - Each allocated block is registered in an alloc[] array of unsigned char.
// - This registration is required by the free() operation, because the size
//   of the allocated block must be obtained from the base address of the block.  
// - The number of entries in this array is equal to the max number
//   of allocated block : MALLOC_LOCAL_STORE_SIZE / MALLOC_MIN_BLOCK_SIZE.
// - For each allocated block, the value registered in the alloc[] array
//   is log2( size_of_allocated_block ).
// - The index in this array is computed from the allocated block base address:
//      index = (block_base - vseg_base) / MALLOC_MIN_BLOCK_SIZE
// - The alloc[] array is stored at the end of heap segment. This consume
//   (1 / MALLOC_MIN_BLOCK_SIZE) of the total storage capacity.
/////////////////////////////////////////////////////////////////////////////////////////


#define MALLOC_INITIALIZED         0xBABEF00D   // magic number when initialised
#define MALLOC_MIN_BLOCK_SIZE      0x40         // 64 bytes
#define MALLOC_LOCAL_STORE_SIZE    0x800000     // 8 Mbytes     
#define MALLOC_MAX_CLUSTERS        0x100        // 256 clusters

/////////////////////////////////////////////////////////////////////////////////////////
//               store(x,y) descriptor (one per cluster)
/////////////////////////////////////////////////////////////////////////////////////////

typedef struct malloc_store_s
{
    pthread_mutex_t mutex;           // lock protecting exclusive access to local heap
    unsigned int    initialized;     // initialised when value == MALLOC_INITIALIZED
    unsigned int    cxy;             // cluster identifier  
    unsigned int    store_base;      // store base address
    unsigned int    store_size;      // store size (bytes)
    unsigned int    alloc_base;      // alloc[] array base address
    unsigned int    alloc_size;      // alloc[] array size (bytes)
    unsigned int    free[32];        // array of addresses of first free block 
} 
malloc_store_t;

/*****************************************************************************************
 * This function allocates <size> bytes of memory in user space, and returns a pointer
 * to the allocated buffer. The pysical memory is allocated from store located in 
 * cluster identified by the <cxy> argument.
 *****************************************************************************************
 * @ size    : number of requested bytes.
 * @ cxy     : target cluster identifier.
 * @ returns a pointer on the allocated buffer if success / returns NULL if failure
 ****************************************************************************************/
void * remote_malloc( unsigned int size, 
                      unsigned int cxy );

/*****************************************************************************************
 * This function releases the memory buffer identified by the <ptr> argument,
 * to the store identified by the <cxy> argument.
 * It displays an error message, but does nothing if the ptr is illegal.
 *****************************************************************************************
 * @ ptr   : pointer on the released buffer.
 * @ cxy   : target cluster identifier.
 ****************************************************************************************/
void remote_free( void        * ptr,
                  unsigned int  cxy );

/*****************************************************************************************
 * This function releases the memory buffer identified by the <ptr> argument,
 * to the store located in cluster identified by the <cxy> argument, and allocates
 * a new buffer containing <size> bytes from this store. 
 * The content of the old buffer is copied to the new buffer, up to <size> bytes.
 * It displays an error message, but does nothing if the ptr is illegal.
 *****************************************************************************************
 * @ ptr     : pointer on the released buffer.
 * @ size    : new buffer requested size (bytes).
 * @ cxy     : target cluster identifier.
 * @ return a pointer on allocated buffer if success / return NULL if failure
 ****************************************************************************************/
void * remote_realloc( void        * ptr,
                       unsigned int  size,
                       unsigned int  cxy );

/*****************************************************************************************
 * This function allocates enough space for <count> objects that are <size> bytes
 * of memory each from the store located in cluster identied by the <cxy> argument.
 * The allocated memory is filled with bytes of value zero.
 *****************************************************************************************
 * @ count   : number of requested objects.
 * @ size    : number of bytes per object.
 * @ cxy     : target cluster identifier.
 * @ returns a pointer on allocated buffer if success / returns NULL if failure
 ****************************************************************************************/
void * remote_calloc( unsigned int count,
                      unsigned int size,
                      unsigned int cxy );

#endif /* _LIBALMOSMKH_H_ */

