Index: trunk/kernel/mm/vmm.c
===================================================================
--- trunk/kernel/mm/vmm.c	(revision 580)
+++ trunk/kernel/mm/vmm.c	(revision 585)
@@ -47,4 +47,5 @@
 #include <kmem.h>
 #include <vmm.h>
+#include <hal_exception.h>
 
 //////////////////////////////////////////////////////////////////////////////////
@@ -162,5 +163,8 @@
     printk("\n[ERROR] in %s : cannot create GPT\n", __FUNCTION__ );
 
-    // initialize GPT (architecture specic)
+    // initialize GPT lock
+    remote_rwlock_init( XPTR( local_cxy , &vmm->gpt_lock ) , LOCK_VMM_GPT );
+
+    // architecture specic GPT initialisation
     // (For TSAR, identity map the kentry_vseg)
     error = hal_vmm_init( vmm );
@@ -209,6 +213,7 @@
     process->vmm.gpt.ptr , process->pid , local_cxy );
 
-    // get lock protecting the vseg list
+    // get lock protecting the VSL and the GPT
     remote_rwlock_rd_acquire( XPTR( local_cxy , &vmm->vsegs_lock ) );
+    remote_rwlock_rd_acquire( XPTR( local_cxy , &vmm->gpt_lock ) );
 
     // scan the list of vsegs
@@ -234,5 +239,5 @@
             for( vpn = base ; vpn < (base+size) ; vpn++ )
             {
-                hal_gpt_get_pte( gpt , vpn , &attr , &ppn );
+                hal_gpt_get_pte( XPTR( local_cxy , gpt ) , vpn , &attr , &ppn );
                 if( attr & GPT_MAPPED )
                 {
@@ -243,6 +248,7 @@
     }
 
-    // release the lock
+    // release the locks
     remote_rwlock_rd_release( XPTR( local_cxy , &vmm->vsegs_lock ) );
+    remote_rwlock_rd_release( XPTR( local_cxy , &vmm->gpt_lock ) );
 
 }  // vmm_display()
@@ -295,5 +301,4 @@
                             ppn_t       ppn )
 {
-
     xlist_entry_t * process_root_ptr;
     xptr_t          process_root_xp;
@@ -317,6 +322,5 @@
 
 // check cluster is reference
-assert( (GET_CXY( process->ref_xp ) == local_cxy) ,
-"not called in reference cluster\n");
+assert( (GET_CXY( process->ref_xp ) == local_cxy) , "not called in reference cluster\n");
 
     // get extended pointer on root of process copies xlist in owner cluster
@@ -465,12 +469,9 @@
                 if( remote_process_cxy == local_cxy )
                 {
-                    // the reference GPT is the local GPT
-                    gpt_t * gpt = GET_PTR( remote_gpt_xp );
-
                     // scan all pages in vseg 
                     for( vpn = vpn_base ; vpn < (vpn_base + vpn_size) ; vpn++ )
                     {
                         // get page attributes and PPN from reference GPT
-                        hal_gpt_get_pte( gpt , vpn , &attr , &ppn ); 
+                        hal_gpt_get_pte( remote_gpt_xp , vpn , &attr , &ppn ); 
 
                         // atomically update pending forks counter if page is mapped
@@ -1139,5 +1140,5 @@
     {
         // get GPT entry
-        hal_gpt_get_pte( gpt , vpn , &attr , &ppn );
+        hal_gpt_get_pte( XPTR( local_cxy , gpt ) , vpn , &attr , &ppn );
 
         if( attr & GPT_MAPPED )  // entry is mapped
@@ -1150,8 +1151,7 @@
 
 // check small page
-assert( (attr & GPT_SMALL) ,
-"an user vseg must use small pages" );
-
-            // unmap GPT entry in all GPT copies
+assert( (attr & GPT_SMALL) , "an user vseg must use small pages" );
+
+            // unmap GPT entry in local GPT
             hal_gpt_reset_pte( gpt , vpn );
 
@@ -1392,4 +1392,5 @@
 // for a given <vpn> in a given <vseg>, allocates the page (with an RPC if required)
 // and returns an extended pointer on the allocated page descriptor.
+// It can be called by a thread running in any cluster.
 // The vseg cannot have the FILE type.
 //////////////////////////////////////////////////////////////////////////////////////
@@ -1655,134 +1656,73 @@
 }  // end vmm_get_one_ppn()
 
-/////////////////////////////////////////
-error_t vmm_get_pte( process_t * process,
-                     vpn_t       vpn,
-                     bool_t      cow,
-                     uint32_t  * attr,
-                     ppn_t     * ppn )
+///////////////////////////////////////////////////
+error_t vmm_handle_page_fault( process_t * process,
+                               vpn_t       vpn )
 {
-    ppn_t      old_ppn;    // current PTE_PPN
-    uint32_t   old_attr;   // current PTE_ATTR
-    ppn_t      new_ppn;    // new PTE_PPN
-    uint32_t   new_attr;   // new PTE_ATTR
-    vmm_t    * vmm;
-    vseg_t   * vseg;      
-    error_t    error;
-
-
-#if DEBUG_VMM_GET_PTE
+    vseg_t         * vseg;            // vseg containing vpn
+    uint32_t         new_attr;        // new PTE_ATTR value
+    ppn_t            new_ppn;         // new PTE_PPN value
+    uint32_t         ref_attr;        // PTE_ATTR value in reference GPT
+    ppn_t            ref_ppn;         // PTE_PPN value in reference GPT
+    cxy_t            ref_cxy;         // reference cluster for missing vpn
+    process_t      * ref_ptr;         // reference process for missing vpn
+    xptr_t           local_gpt_xp;    // extended pointer on local GPT
+    xptr_t           local_lock_xp;   // extended pointer on local GPT lock
+    xptr_t           ref_gpt_xp;      // extended pointer on reference GPT
+    xptr_t           ref_lock_xp;     // extended pointer on reference GPT lock
+    error_t          error;           // value returned by called functions
+
+    // get local vseg (access to reference VSL can be required)
+    error = vmm_get_vseg( process, 
+                          (intptr_t)vpn<<CONFIG_PPM_PAGE_SHIFT,
+                          &vseg );
+
+    if( error )
+    {
+        printk("\n[ERROR] in %s : illegal VPN %x in process %x\n",
+        __FUNCTION__ , vpn , process->pid );
+        
+        return EXCP_USER_ERROR;
+    }
+
+ #if DEBUG_VMM_HANDLE_PAGE_FAULT
+uint32_t   cycle = (uint32_t)hal_get_cycles();
 thread_t * this  = CURRENT_THREAD;
-uint32_t   cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_VMM_GET_PTE < cycle )
-printk("\n[DBG] %s : thread %x in process %x enter / vpn %x / cow %d / cycle %d\n",
-__FUNCTION__, this->trdid, process->pid, vpn, cow, cycle );
-#endif
-
-    // get VMM pointer
-    vmm = &process->vmm;
-
-    // get local vseg descriptor
-    error =  vmm_get_vseg( process, 
-                           ((intptr_t)vpn << CONFIG_PPM_PAGE_SHIFT), 
-                           &vseg );
-
-// vseg has been checked by the vmm_handle_page_fault() function
-assert( (vseg != NULL) , "vseg undefined / vpn %x\n");
-
-    if( cow )  //////////////// copy_on_write request //////////////////////
-               // get PTE from local GPT
-               // allocate a new physical page if there is pending forks,
-               // initialize it from old physical page content,
-               // update PTE in all GPT copies, 
-    {
-        // access local GPT to get current PTE attributes and PPN
-        hal_gpt_get_pte( &vmm->gpt , vpn , &old_attr , &old_ppn );
-
-assert( (old_attr & GPT_MAPPED),
-"PTE unmapped for a COW exception / vpn %x\n" );
-
-#if( DEBUG_VMM_GET_PTE & 1 )
-if( DEBUG_VMM_GET_PTE < cycle )
-printk("\n[DBG] %s : thread %x in process %x handling COW for vpn %x\n",
-__FUNCTION__, this->trdid, process->pid, vpn );
-#endif
-
-        // get extended pointer, cluster and local pointer on physical page descriptor
-        xptr_t   page_xp  = ppm_ppn2page( old_ppn );
-        cxy_t    page_cxy = GET_CXY( page_xp );
-        page_t * page_ptr = GET_PTR( page_xp );
-
-        // get extended pointers on forks and lock field in page descriptor
-        xptr_t forks_xp = XPTR( page_cxy , &page_ptr->forks );
-        xptr_t lock_xp  = XPTR( page_cxy , &page_ptr->lock );
-
-        // take lock protecting "forks" counter
-        remote_busylock_acquire( lock_xp );
-
-        // get number of pending forks in page descriptor
-        uint32_t forks = hal_remote_l32( forks_xp );
-
-        if( forks )        // pending fork => allocate a new page, copy old to new
-        {
-            // allocate a new physical page
-            page_xp = vmm_page_allocate( vseg , vpn );
-            if( page_xp == XPTR_NULL ) 
-            {
-                printk("\n[ERROR] in %s : no memory / process = %x / vpn = %x\n",
-                __FUNCTION__ , process->pid , vpn );
-                return -1;
-            }
-
-            // compute allocated page PPN 
-            new_ppn = ppm_page2ppn( page_xp );
-
-            // copy old page content to new page
-            xptr_t  old_base_xp = ppm_ppn2base( old_ppn );
-            xptr_t  new_base_xp = ppm_ppn2base( new_ppn );
-            memcpy( GET_PTR( new_base_xp ),
-                    GET_PTR( old_base_xp ),
-                    CONFIG_PPM_PAGE_SIZE );
-
-             // decrement pending forks counter in page descriptor
-             hal_remote_atomic_add( forks_xp , -1 );
-        }             
-        else               // no pending fork => keep the existing page
-        {
-            new_ppn = old_ppn;
-        }
-
-        // release lock protecting "forks" counter
-        remote_busylock_release( lock_xp );
-
-        // build new_attr : reset COW and set WRITABLE,
-        new_attr = (old_attr | GPT_WRITABLE) & (~GPT_COW);
-
-        // update GPT[vpn] for all GPT copies
-        vmm_global_update_pte( process, vpn, new_attr, new_ppn );
-    }
-    else        //////////// page_fault request ///////////////////////////
-                // get PTE from local GPT
-                // allocate a physical page if it is a true page fault,
-                // initialize it if type is FILE, CODE, or DATA,
-                // register in reference GPT, but don't update GPT copies
-    {  
-        // access local GPT to get current PTE 
-        hal_gpt_get_pte( &vmm->gpt , vpn , &old_attr , &old_ppn );
-
-        if( (old_attr & GPT_MAPPED) == 0 )   // true page_fault => map it
-        {
-
-#if( DEBUG_VMM_GET_PTE & 1 )
-if( DEBUG_VMM_GET_PTE < cycle )
-printk("\n[DBG] %s : thread %x in process %x handling page fault for vpn %x\n",
-__FUNCTION__, this->trdid, process->pid, vpn );
-#endif
-            // allocate new_ppn, and initialize the new page
+if( DEBUG_VMM_HANDLE_PAGE_FAULT < cycle )
+printk("\n[DBG] %s : threadr[%x,%x] enter for vpn %x / %s / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, vpn, vseg_type_str(vseg->type), cycle );
+#endif
+
+    //////////////// private vseg => access only the local GPT 
+    if( (vseg->type == VSEG_TYPE_STACK) || (vseg->type == VSEG_TYPE_CODE) )
+    {
+        // build extended pointer on local GPT and local GPT lock
+        local_gpt_xp  = XPTR( local_cxy , &process->vmm.gpt );
+        local_lock_xp = XPTR( local_cxy , &process->vmm.gpt_lock );
+
+        // take local GPT lock in write mode
+        remote_rwlock_wr_acquire( local_lock_xp );
+
+        // check VPN still unmapped in local GPT
+        // do nothing if VPN has been mapped by a a concurrent page_fault
+        hal_gpt_get_pte( local_gpt_xp,
+                         vpn,
+                         &new_attr,
+                         &new_ppn );
+
+        if( (new_attr & GPT_MAPPED) == 0 )       // VPN still unmapped
+        { 
+            // allocate and initialise a physical page depending on the vseg type
             error = vmm_get_one_ppn( vseg , vpn , &new_ppn );
+
             if( error )
             {
                 printk("\n[ERROR] in %s : no memory / process = %x / vpn = %x\n",
                 __FUNCTION__ , process->pid , vpn );
-                return -1;
+
+                // release local GPT lock in write mode
+                remote_rwlock_wr_release( local_lock_xp );
+
+                return EXCP_KERNEL_PANIC;
             }
 
@@ -1794,176 +1734,368 @@
             if( vseg->flags & VSEG_CACHE ) new_attr |= GPT_CACHABLE;
 
-            // register new PTE in reference GPT
-            // on demand policy => no update of GPT copies
-            error = hal_gpt_set_pte( &vmm->gpt,
+            // set PTE (PPN & attribute) to local GPT
+            error = hal_gpt_set_pte( local_gpt_xp,
                                      vpn,
                                      new_attr,
                                      new_ppn );
-            if( error )
+            if ( error )
             {
-                printk("\n[ERROR] in %s : cannot update GPT / process = %x / vpn = %x\n",
+                printk("\n[ERROR] in %s : cannot update local GPT / process %x / vpn = %x\n",
                 __FUNCTION__ , process->pid , vpn );
-                return -1;
+
+                // release local GPT lock in write mode
+                remote_rwlock_wr_release( local_lock_xp );
+
+                return EXCP_KERNEL_PANIC;
             }
         }
-        else                                  // mapped in reference GPT => get it
-        {
-            new_ppn  = old_ppn;
-            new_attr = old_attr;
-        }
-    }
-
-#if DEBUG_VMM_GET_PTE
-cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_VMM_GET_PTE < cycle )
-printk("\n[DBG] %s : thread %x in process %x exit / vpn %x / ppn %x / attr %x / cycle %d\n",
-__FUNCTION__, this->trdid, process->pid, vpn, new_ppn, new_attr, cycle );
-#endif
-
-    // return PPN and flags
-    *ppn  = new_ppn;
-    *attr = new_attr;
-    return 0;
-
-}  // end vmm_get_pte()
-
-///////////////////////////////////////////////////
-error_t vmm_handle_page_fault( process_t * process,
-                               vpn_t       vpn,
-                               bool_t      is_cow )
-{
-    uint32_t         attr;          // missing page attributes
-    ppn_t            ppn;           // missing page PPN
-    vseg_t         * vseg;          // vseg containing vpn
-    uint32_t         type;          // vseg type
-    cxy_t            ref_cxy;       // reference cluster for missing vpn
-    process_t      * ref_ptr;       // reference process for missing vpn
-    error_t          error;
-
-    thread_t       * this = CURRENT_THREAD;
-
-#if DEBUG_VMM_HANDLE_PAGE_FAULT
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_VMM_HANDLE_PAGE_FAULT < cycle )
-printk("\n[DBG] %s : thread %x in process %x enter for vpn %x / core[%x,%d] / cycle %d\n",
-__FUNCTION__, this, process->pid, vpn, local_cxy, this->core->lid, cycle );
-#endif
-
-    // get local vseg (access reference VSL if required)
-    error = vmm_get_vseg( process , vpn<<CONFIG_PPM_PAGE_SHIFT , &vseg );
-
-    if( error )
-    {
-        printk("\n[ERROR] in %s : vpn %x / process %x / thread %x / core[%x,%d] / cycle %d\n",
-        __FUNCTION__, vpn, process->pid, this->trdid, local_cxy, this->core->lid,
-        (uint32_t)hal_get_cycles() );
-        return error;
-    }
-
-    // get segment type
-    type = vseg->type;
-
-    // get relevant process cluster and local pointer
-    // for private vsegs (CODE and DATA type), 
-    // the relevant process descriptor is local.
-    if( (type == VSEG_TYPE_STACK) || (type == VSEG_TYPE_CODE) )
-    {
-        ref_cxy = local_cxy;
-        ref_ptr = process;
-    }
-    else
-    {
-        ref_cxy = GET_CXY( process->ref_xp );
-        ref_ptr = GET_PTR( process->ref_xp );
-    }
-
-    // get missing PTE attributes and PPN 
-    if( local_cxy != ref_cxy )  
-    {
-
-#if DEBUG_VMM_HANDLE_PAGE_FAULT
-if( DEBUG_VMM_HANDLE_PAGE_FAULT < cycle )
-printk("\n[DBG] %s : thread %x in process %x call RPC_VMM_GET_PTE\n",
-__FUNCTION__, this, process->pid );
-#endif
-
-        rpc_vmm_get_pte_client( ref_cxy,
-                                ref_ptr,
-                                vpn,
-                                is_cow,
-                                &attr,
-                                &ppn,
-                                &error );
-
-        // get local VMM pointer
-        vmm_t * vmm = &process->vmm;
-
-        // update local GPT
-        error |= hal_gpt_set_pte( &vmm->gpt,
-                                  vpn,
-                                  attr,
-                                  ppn );
-    }
-    else   // local cluster is the reference cluster
-    {
-        error = vmm_get_pte( process,
-                             vpn,
-                             is_cow,
-                             &attr,
-                             &ppn );
-    }
+
+        // release local GPT lock in write mode
+        remote_rwlock_wr_release( local_lock_xp );
 
 #if DEBUG_VMM_HANDLE_PAGE_FAULT
 cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_VMM_HANDLE_PAGE_FAULT < cycle )
-printk("\n[DBG] %s : thread %x in process %x exit for vpn %x / core[%x,%d] / cycle %d\n",
-__FUNCTION__, this, process->pid, vpn, local_cxy, this->core->lid, cycle );
-#endif
-
-    return error;
-
+printk("\n[DBG] %s : private page fault handled / vpn %x / ppn %x / attr %x / cycle %d\n",
+__FUNCTION__, vpn, new_ppn, new_attr, cycle );
+#endif
+        return EXCP_NON_FATAL;
+
+    }   // end local GPT access 
+
+    //////////// public vseg => access reference GPT
+    else                                
+    {
+        // get reference process cluster and local pointer
+        ref_cxy = GET_CXY( process->ref_xp );
+        ref_ptr = GET_PTR( process->ref_xp );
+
+        // build extended pointer on reference GPT and reference GPT lock
+        ref_gpt_xp  = XPTR( ref_cxy , &ref_ptr->vmm.gpt );
+        ref_lock_xp = XPTR( ref_cxy , &ref_ptr->vmm.gpt_lock );
+
+        // build extended pointer on local GPT and local GPT lock
+        local_gpt_xp  = XPTR( local_cxy , &process->vmm.gpt );
+        local_lock_xp = XPTR( local_cxy , &process->vmm.gpt_lock );
+
+        // take reference GPT lock in read mode 
+        remote_rwlock_rd_acquire( ref_lock_xp );
+
+        // get directly PPN & attributes from reference GPT
+        // this can avoids a costly RPC for a false page fault
+        hal_gpt_get_pte( ref_gpt_xp,
+                         vpn,
+                         &ref_attr,
+                         &ref_ppn );
+
+        // release reference GPT lock in read mode 
+        remote_rwlock_rd_release( ref_lock_xp );
+
+        if( ref_attr & GPT_MAPPED )        // false page fault => update local GPT
+        {
+            // take local GPT lock in write mode
+            remote_rwlock_wr_acquire( local_lock_xp );
+            
+            // check VPN still unmapped in local GPT
+            hal_gpt_get_pte( local_gpt_xp,
+                             vpn,
+                             &new_attr,
+                             &new_ppn );
+
+            if( (new_attr & GPT_MAPPED) == 0 )       // VPN still unmapped
+            { 
+                // update local GPT from reference GPT
+                error = hal_gpt_set_pte( local_gpt_xp,
+                                         vpn,
+                                         ref_attr,
+                                         ref_ppn );
+                if( error )
+                {
+                    printk("\n[ERROR] in %s : cannot update local GPT / process %x / vpn = %x\n",
+                    __FUNCTION__ , process->pid , vpn );
+
+                    // release local GPT lock in write mode
+                    remote_rwlock_wr_release( local_lock_xp );
+            
+                    return EXCP_KERNEL_PANIC;
+                }
+            }
+            else    // VPN has been mapped by a a concurrent page_fault 
+            {
+                // keep PTE from local GPT
+                ref_attr = new_attr;
+                ref_ppn  = new_ppn;
+            }
+
+            // release local GPT lock in write mode
+            remote_rwlock_wr_release( local_lock_xp );
+            
+#if DEBUG_VMM_HANDLE_PAGE_FAULT
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_VMM_HANDLE_PAGE_FAULT < cycle )
+printk("\n[DBG] %s : false page fault handled / vpn %x / ppn %x / attr %x / cycle %d\n",
+__FUNCTION__, vpn, ref_ppn, ref_attr, cycle );
+#endif
+            return EXCP_NON_FATAL;
+        }
+        else                            // true page fault => update reference GPT
+        {
+            // take reference GPT lock in write mode
+            remote_rwlock_wr_acquire( ref_lock_xp );
+            
+            // check VPN still unmapped in reference GPT
+            // do nothing if VPN has been mapped by a a concurrent page_fault
+            hal_gpt_get_pte( ref_gpt_xp,
+                             vpn,
+                             &ref_attr,
+                             &ref_ppn );
+
+            if( (ref_attr & GPT_MAPPED) == 0 )       // VPN actually unmapped
+            { 
+                // allocate and initialise a physical page depending on the vseg type
+                error = vmm_get_one_ppn( vseg , vpn , &new_ppn );
+
+                if( error )
+                {
+                    printk("\n[ERROR] in %s : no memory / process = %x / vpn = %x\n",
+                    __FUNCTION__ , process->pid , vpn );
+
+                   // release reference GPT lock in write mode
+                   remote_rwlock_wr_release( ref_lock_xp );
+                    
+                   return EXCP_KERNEL_PANIC;
+                }
+
+                // define new_attr from vseg flags 
+                new_attr = GPT_MAPPED | GPT_SMALL;
+                if( vseg->flags & VSEG_USER  ) new_attr |= GPT_USER;
+                if( vseg->flags & VSEG_WRITE ) new_attr |= GPT_WRITABLE;
+                if( vseg->flags & VSEG_EXEC  ) new_attr |= GPT_EXECUTABLE;
+                if( vseg->flags & VSEG_CACHE ) new_attr |= GPT_CACHABLE;
+
+                // update reference GPT
+                error = hal_gpt_set_pte( ref_gpt_xp,
+                                         vpn,
+                                         new_attr,
+                                         new_ppn );
+
+                // update local GPT (protected by reference GPT lock)
+                error |= hal_gpt_set_pte( local_gpt_xp,
+                                          vpn,
+                                          new_attr,
+                                          new_ppn );
+
+                if( error )
+                {
+                    printk("\n[ERROR] in %s : cannot update GPT / process %x / vpn = %x\n",
+                    __FUNCTION__ , process->pid , vpn );
+
+                    // release reference GPT lock in write mode
+                    remote_rwlock_wr_release( ref_lock_xp );
+
+                    return EXCP_KERNEL_PANIC;
+                }
+            }
+
+            // release reference GPT lock in write mode
+            remote_rwlock_wr_release( ref_lock_xp );
+
+#if DEBUG_VMM_HANDLE_PAGE_FAULT
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_VMM_HANDLE_PAGE_FAULT < cycle )
+printk("\n[DBG] %s : true page fault handled / vpn %x / ppn %x / attr %x / cycle %d\n",
+__FUNCTION__, vpn, new_ppn, new_attr, cycle );
+#endif
+            return EXCP_NON_FATAL;
+        }
+    }
 }   // end vmm_handle_page_fault()
 
-
-
-
-
-
-
-
-
-/* deprecated April 2018  [AG]
-
-error_t vmm_v2p_translate( process_t * process,
-                           void      * ptr,
-                           paddr_t   * paddr )
+////////////////////////////////////////////
+error_t vmm_handle_cow( process_t * process,
+                        vpn_t       vpn )
 {
-    // access page table
-    error_t  error;
-    vpn_t    vpn;
-    uint32_t attr;
-    ppn_t    ppn;
-    uint32_t offset;
-
-    vpn    = (vpn_t)( (intptr_t)ptr >> CONFIG_PPM_PAGE_SHIFT );
-    offset = (uint32_t)( ((intptr_t)ptr) & CONFIG_PPM_PAGE_MASK );
-
-    if( local_cxy == GET_CXY( process->ref_xp) ) // local process is reference process
-    {
-        error = vmm_get_pte( process, vpn , false , &attr , &ppn );
-    }
-    else                                         // calling process is not reference process
-    {
-        cxy_t       ref_cxy = GET_CXY( process->ref_xp );
-        process_t * ref_ptr = GET_PTR( process->ref_xp );
-        rpc_vmm_get_pte_client( ref_cxy , ref_ptr , vpn , false , &attr , &ppn , &error );
-    }
-
-    // set paddr
-    *paddr = (((paddr_t)ppn) << CONFIG_PPM_PAGE_SHIFT) | offset;
-
-    return error;
-
-}  // end vmm_v2p_translate()
-
-*/
+    vseg_t         * vseg;            // vseg containing vpn
+    cxy_t            ref_cxy;         // reference cluster for missing vpn
+    process_t      * ref_ptr;         // reference process for missing vpn
+    xptr_t           gpt_xp;          // extended pointer on GPT
+    xptr_t           gpt_lock_xp;     // extended pointer on GPT lock
+    uint32_t         old_attr;        // current PTE_ATTR value
+    ppn_t            old_ppn;         // current PTE_PPN value
+    uint32_t         new_attr;        // new PTE_ATTR value
+    ppn_t            new_ppn;         // new PTE_PPN value
+    error_t          error;
+
+#if DEBUG_VMM_HANDLE_COW
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+thread_t * this  = CURRENT_THREAD;
+if( DEBUG_VMM_HANDLE_COW < cycle )
+printk("\n[DBG] %s : thread[%x,%x] enter for vpn %x / core[%x,%d] / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, vpn, local_cxy, this->core->lid, cycle );
+#endif
+
+    // get local vseg
+    error = vmm_get_vseg( process, 
+                          (intptr_t)vpn<<CONFIG_PPM_PAGE_SHIFT,
+                          &vseg );
+
+    if( error )
+    {
+        printk("\n[PANIC] in %s : VPN %x in process %x not in registered vseg\n",
+        __FUNCTION__, vpn, process->pid );
+
+        return EXCP_KERNEL_PANIC;
+    }
+
+    // get reference GPT cluster and local pointer
+    ref_cxy = GET_CXY( process->ref_xp );
+    ref_ptr = GET_PTR( process->ref_xp );
+
+    // build relevant extended pointers on  GPT and  GPT lock
+    // - access local GPT for a private vseg  
+    // - access reference GPT for a public vseg
+    if( (vseg->type == VSEG_TYPE_STACK) || (vseg->type == VSEG_TYPE_CODE) )
+    {
+        gpt_xp      = XPTR( local_cxy , &process->vmm.gpt );
+        gpt_lock_xp = XPTR( local_cxy , &process->vmm.gpt_lock );
+    }
+    else
+    {
+        gpt_xp      = XPTR( ref_cxy , &ref_ptr->vmm.gpt );
+        gpt_lock_xp = XPTR( ref_cxy , &ref_ptr->vmm.gpt_lock );
+    }
+
+    // take GPT lock in write mode
+    remote_rwlock_wr_acquire( gpt_lock_xp );
+
+    // get current PTE from reference GPT
+    hal_gpt_get_pte( gpt_xp,
+                     vpn,
+                     &old_attr,
+                     &old_ppn );
+
+    // the PTE must be mapped for a COW
+    if( (old_attr & GPT_MAPPED) == 0 )
+    {
+        printk("\n[PANIC] in %s : VPN %x in process %x unmapped\n",
+        __FUNCTION__, vpn, process->pid );
+
+        // release GPT lock in write mode
+        remote_rwlock_wr_acquire( gpt_lock_xp );
+
+        return EXCP_KERNEL_PANIC;
+    }
+
+    // get extended pointer, cluster and local pointer on physical page descriptor
+    xptr_t   page_xp  = ppm_ppn2page( old_ppn );
+    cxy_t    page_cxy = GET_CXY( page_xp );
+    page_t * page_ptr = GET_PTR( page_xp );
+
+    // get extended pointers on forks and lock field in page descriptor
+    xptr_t forks_xp       = XPTR( page_cxy , &page_ptr->forks );
+    xptr_t forks_lock_xp  = XPTR( page_cxy , &page_ptr->lock );
+
+    // take lock protecting "forks" counter
+    remote_busylock_acquire( forks_lock_xp );
+
+    // get number of pending forks from page descriptor
+    uint32_t forks = hal_remote_l32( forks_xp );
+
+    if( forks )        // pending fork => allocate a new page, and copy old to new
+    {
+        // allocate a new physical page
+        page_xp = vmm_page_allocate( vseg , vpn );
+        if( page_xp == XPTR_NULL ) 
+        {
+            printk("\n[PANIC] in %s : no memory for vpn %x in process %x\n",
+            __FUNCTION__ , vpn, process->pid );
+
+            // release GPT lock in write mode
+            remote_rwlock_wr_acquire( gpt_lock_xp );
+
+            // release lock protecting "forks" counter
+            remote_busylock_release( forks_lock_xp );
+
+            return EXCP_KERNEL_PANIC;
+        }
+
+        // compute allocated page PPN 
+        new_ppn = ppm_page2ppn( page_xp );
+
+        // copy old page content to new page
+        xptr_t  old_base_xp = ppm_ppn2base( old_ppn );
+        xptr_t  new_base_xp = ppm_ppn2base( new_ppn );
+        memcpy( GET_PTR( new_base_xp ),
+                GET_PTR( old_base_xp ),
+                CONFIG_PPM_PAGE_SIZE );
+
+        // decrement pending forks counter in page descriptor
+        hal_remote_atomic_add( forks_xp , -1 );
+
+#if(DEBUG_VMM_HANDLE_COW & 1)
+if( DEBUG_VMM_HANDLE_COW < cycle )
+printk("\n[DBG] %s : thread[%x,%x] : pending forks => allocate a new PPN %x\n",
+__FUNCTION__, process->pid, this->trdid, new_ppn );
+#endif
+
+    }             
+    else               // no pending fork => keep the existing page
+    {
+
+#if(DEBUG_VMM_HANDLE_COW & 1)
+if( DEBUG_VMM_HANDLE_COW < cycle )
+printk("\n[DBG] %s : thread[%x,%x]  no pending forks => keep existing PPN %x\n",
+__FUNCTION__, process->pid, this->trdid, new_ppn );
+#endif
+        new_ppn = old_ppn;
+    }
+
+    // release lock protecting "forks" counter
+    remote_busylock_release( forks_lock_xp );
+
+    // build new_attr : reset COW and set WRITABLE,
+    new_attr = (old_attr | GPT_WRITABLE) & (~GPT_COW);
+
+    // update the relevan GPT
+    // - private vseg => update local GPT 
+    // - public vseg => update all GPT copies
+    if( (vseg->type == VSEG_TYPE_STACK) || (vseg->type == VSEG_TYPE_CODE) )
+    {
+        hal_gpt_set_pte( gpt_xp,
+                         vpn,
+                         new_attr,
+                         new_ppn );
+    }
+    else
+    {
+        if( ref_cxy == local_cxy )                  // reference cluster is local
+        {
+            vmm_global_update_pte( process,
+                                   vpn,
+                                   new_attr,
+                                   new_ppn );
+        }
+        else                                        // reference cluster is remote
+        {
+            rpc_vmm_global_update_pte_client( ref_cxy,
+                                              ref_ptr,
+                                              vpn,
+                                              new_attr,
+                                              new_ppn );
+        }
+    }
+
+    // release GPT lock in write mode
+    remote_rwlock_wr_release( gpt_lock_xp );
+
+#if DEBUG_VMM_HANDLE_COW
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_VMM_HANDLE_COW < cycle )
+printk("\n[DBG] %s : thread[%x,%x] exit for vpn %x / core[%x,%d] / cycle %d\n",
+__FUNCTION__, process->pid, this->trdid, vpn, local_cxy, this->core->lid, cycle );
+#endif
+
+     return EXCP_NON_FATAL;
+
+}   // end vmm_handle_cow()
+
