/*
 * vseg.c - virtual segment (vseg) related operations
 *
 * Authors   Ghassan Almaless (2008,2009,2010,2011, 2012)
 *           Mohamed Lamine Karaoui (2015)
 *           Alain Greiner (2016)
 *
 * 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
 */

#include <hal_types.h>
#include <hal_special.h>
#include <hal_remote.h>
#include <list.h>
#include <errno.h>
#include <printk.h>
#include <bits.h>
#include <thread.h>
#include <process.h>
#include <ppm.h>
#include <mapper.h>
#include <spinlock.h>
#include <vfs.h>
#include <page.h>
#include <vmm.h>
#include <kmem.h>
#include <vseg.h>

////////////////////////////////////////////////////////////////////////////////////////
//   global variables for display / must be consistent with enum in "vseg.h"
////////////////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////
char * vseg_type_str( uint32_t vseg_type )
{
  	if     ( vseg_type == VSEG_TYPE_CODE   ) return "CODE";
  	else if( vseg_type == VSEG_TYPE_DATA   ) return "DATA";
  	else if( vseg_type == VSEG_TYPE_HEAP   ) return "HEAP";
  	else if( vseg_type == VSEG_TYPE_STACK  ) return "STACK";
  	else if( vseg_type == VSEG_TYPE_ANON   ) return "ANON";
  	else if( vseg_type == VSEG_TYPE_FILE   ) return "FILE";
  	else if( vseg_type == VSEG_TYPE_REMOTE ) return "REMOTE";
  	else if( vseg_type == VSEG_TYPE_KCODE  ) return "KCODE";
  	else if( vseg_type == VSEG_TYPE_KDATA  ) return "KDATA";
  	else if( vseg_type == VSEG_TYPE_KDEV   ) return "KDEV";
    else                                     return "undefined";
}

/////////////////////
vseg_t * vseg_alloc()
{
    kmem_req_t   req;

    req.type  = KMEM_VSEG;
	req.size  = sizeof(vseg_t);
	req.flags = AF_KERNEL;

    return (vseg_t *)kmem_alloc( &req );
}

///////////////////////////////
void vseg_free( vseg_t * vseg )
{
    kmem_req_t  req;

	req.type = KMEM_VSEG;
	req.ptr  = vseg;
	kmem_free( &req );
}

///////////////////////////////////
void vseg_init( vseg_t      * vseg,
	            intptr_t      base,
                intptr_t      size,
                vpn_t         vpn_base,
                vpn_t         vpn_size,
		        uint32_t      type,
                cxy_t         cxy )
{
    vseg->type      = type;
	vseg->min       = base;
	vseg->max       = base + size;
    vseg->vpn_base  = vpn_base;
	vseg->vpn_size  = vpn_size;
	vseg->mapper_xp = XPTR_NULL;
    vseg->cxy       = cxy;

    // set vseg flags depending on type
	if     ( type == VSEG_TYPE_CODE )
    {
        vseg->flags = VSEG_USER    |
                      VSEG_EXEC    |
                      VSEG_CACHE   |
                      VSEG_PRIVATE ;
    }
    else if( type == VSEG_TYPE_STACK )
    {
        vseg->flags = VSEG_USER    |
                      VSEG_WRITE   |
                      VSEG_CACHE   |
                      VSEG_PRIVATE ;
    }
    else if( type == VSEG_TYPE_DATA )
    {
        vseg->flags = VSEG_USER    |
                      VSEG_WRITE   |
                      VSEG_CACHE   |
                      VSEG_DISTRIB ;
    }
    else if( type == VSEG_TYPE_HEAP )
    {
        vseg->flags = VSEG_USER    |
                      VSEG_WRITE   |
                      VSEG_CACHE   |
                      VSEG_DISTRIB ;
    }
    else if( type == VSEG_TYPE_REMOTE )
    {
        vseg->flags = VSEG_USER    |
                      VSEG_WRITE   |
                      VSEG_CACHE   ;
    }
    else if( type == VSEG_TYPE_ANON )
    {
        vseg->flags = VSEG_USER    |
                      VSEG_WRITE   |
                      VSEG_CACHE   |
                      VSEG_DISTRIB ;
    }
    else if( type == VSEG_TYPE_FILE )
    {
        vseg->flags = VSEG_USER    |
                      VSEG_WRITE   |
                      VSEG_CACHE   ;
    }
    else if( type == VSEG_TYPE_KCODE )
    {
        vseg->flags = VSEG_EXEC    |
                      VSEG_CACHE   |
                      VSEG_PRIVATE ;
    }
    else if( type == VSEG_TYPE_KDATA )
    {
        vseg->flags = VSEG_WRITE   |
                      VSEG_CACHE   |
                      VSEG_PRIVATE ;
    }
    else
    {
	    printk("\n[PANIC] in %s : illegal vseg type\n", __FUNCTION__);
        hal_core_sleep();
    }

}  // end vseg_init()

//////////////////////////////////////////
void vseg_init_from_ref( vseg_t    * vseg,
                         xptr_t      ref )
{
    // get remote vseg cluster and pointer
    cxy_t    cxy = (cxy_t   )GET_CXY( ref );
    vseg_t * ptr = (vseg_t *)GET_PTR( ref );

    // initialize vseg with remote_read access
    vseg->type       =           hal_remote_lw ( XPTR( cxy , &ptr->type      ) );
    vseg->min        = (intptr_t)hal_remote_lpt( XPTR( cxy , &ptr->min       ) );
    vseg->max        = (intptr_t)hal_remote_lpt( XPTR( cxy , &ptr->max       ) );
    vseg->vpn_base   =           hal_remote_lw ( XPTR( cxy , &ptr->vpn_base  ) );
    vseg->vpn_size   =           hal_remote_lw ( XPTR( cxy , &ptr->vpn_size  ) );
    vseg->flags      =           hal_remote_lw ( XPTR( cxy , &ptr->flags     ) );
	vseg->mapper_xp  = (xptr_t)  hal_remote_lwd( XPTR( cxy , &ptr->mapper_xp ) );
}

///////////////////////////////
error_t vseg_attach( vmm_t  * vmm,
                     vseg_t * vseg )
{
    // add vseg in radix-tree
    error_t error = grdxt_insert( &vmm->grdxt , vseg->vpn_base , vseg );
    if ( error ) return ENOMEM;

    // update vseg descriptor
    vseg->vmm = vmm;

    // add vseg in vmm list
    list_add_last( &vmm->vsegs_root , &vseg->list );

    return 0;
}

///////////////////////////////
void vseg_detach( vmm_t  * vmm,
                  vseg_t * vseg )
{
    // remove vseg from radix-tree
    grdxt_remove( &vmm->grdxt , vseg->vpn_base );

    // update vseg descriptor
    vseg->vmm = NULL;

    // remove vseg from vmm list
    list_unlink( &vseg->list );
}

