Index: trunk/hal/tsar_mips32/core/hal_context.c
===================================================================
--- trunk/hal/tsar_mips32/core/hal_context.c	(revision 624)
+++ trunk/hal/tsar_mips32/core/hal_context.c	(revision 625)
@@ -152,5 +152,5 @@
     {
         context->a0_04   = (uint32_t)thread->entry_args;
-        context->sp_29   = (uint32_t)thread->u_stack_base + (uint32_t)thread->u_stack_size - 8;
+        context->sp_29   = (uint32_t)thread->user_stack_vseg->max - 8;
         context->ra_31   = (uint32_t)&hal_kentry_eret;
         context->c0_epc  = (uint32_t)thread->entry_func;
@@ -175,11 +175,12 @@
 void hal_cpu_context_fork( xptr_t child_xp )
 {
-    // allocate a local CPU context in kernel stack
-    // It is initialized from local parent context
-    // and from child specific values, and is copied in
-    // in the remote child context using a remote_memcpy()
+    // get pointer on calling thread
+    thread_t * this = CURRENT_THREAD;
+
+    // allocate a local CPU context in parent kernel stack
     hal_cpu_context_t  context;
 
-    // get local parent thread local pointer
+    // get local parent thread cluster and local pointer
+    cxy_t      parent_cxy = local_cxy;
     thread_t * parent_ptr = CURRENT_THREAD;
 
@@ -188,53 +189,107 @@
     thread_t * child_ptr = GET_PTR( child_xp );
 
-    // get remote child cpu_context local pointer
+    // get local pointer on remote child cpu context
     char * child_context_ptr = hal_remote_lpt( XPTR(child_cxy , &child_ptr->cpu_context) );
 
     // get local pointer on remote child process
-    process_t * process = (process_t *)hal_remote_lpt( XPTR(child_cxy , &child_ptr->process) );
+    process_t * process = hal_remote_lpt( XPTR(child_cxy , &child_ptr->process) );
 
     // get ppn of remote child process page table
-    uint32_t    pt_ppn = hal_remote_l32( XPTR(child_cxy , &process->vmm.gpt.ppn) );
-
-    // save CPU registers in local CPU context
+    uint32_t pt_ppn = hal_remote_l32( XPTR(child_cxy , &process->vmm.gpt.ppn) );
+
+    // get local pointer on parent uzone from parent thread descriptor
+    uint32_t * parent_uzone = parent_ptr->uzone_current;
+
+    // compute  local pointer on child uzone 
+    uint32_t * child_uzone  = (uint32_t *)( (intptr_t)parent_uzone +
+                                            (intptr_t)child_ptr    -
+                                            (intptr_t)parent_ptr  );
+
+    // update the uzone pointer in child thread descriptor
+    hal_remote_spt( XPTR( child_cxy , &child_ptr->uzone_current ) , child_uzone );
+
+#if DEBUG_HAL_CONTEXT
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_HAL_CONTEXT < cycle )
+printk("\n[%s] thread[%x,%x] parent_uzone %x / child_uzone %x / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, parent_uzone, child_uzone, cycle );
+#endif
+
+    // copy parent kernel stack to child thread descriptor
+    // (this includes the uzone, that is allocated in the kernel stack)
+    char * parent_ksp = (char *)hal_get_sp();
+    char * child_ksp  = (char *)((intptr_t)parent_ksp +
+                                 (intptr_t)child_ptr  - 
+                                 (intptr_t)parent_ptr );
+
+    uint32_t size = (uint32_t)parent_ptr + CONFIG_THREAD_DESC_SIZE - (uint32_t)parent_ksp;
+
+    hal_remote_memcpy( XPTR( child_cxy , child_ksp ),
+                       XPTR( local_cxy , parent_ksp ),
+                       size );
+
+#if DEBUG_HAL_CONTEXT
+cycle = (uint32_t)hal_get_cycles();
+printk("\n[%s] thread[%x,%x] copied kstack from parent %x to child %x / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, parent_ptr, child_ptr, cycle );
+#endif
+
+    // patch the user stack pointer slot in the child uzone[UZ_SP]
+    // because parent and child use the same offset to access the user stack,
+    // but parent and child do not have the same user stack base address.
+    uint32_t parent_us_base = parent_ptr->user_stack_vseg->min;
+    vseg_t * child_us_vseg  = hal_remote_lpt( XPTR( child_cxy , &child_ptr->user_stack_vseg ) );
+    uint32_t child_us_base  = hal_remote_l32( XPTR( child_cxy , &child_us_vseg->min ) );
+    uint32_t parent_usp     = parent_uzone[UZ_SP];
+    uint32_t child_usp      = parent_usp + child_us_base - parent_us_base;
+
+    hal_remote_s32( XPTR( child_cxy , &child_uzone[UZ_SP] ) , child_usp );
+
+#if DEBUG_HAL_CONTEXT
+cycle = (uint32_t)hal_get_cycles();
+printk("\n[%s] thread[%x,%x] parent_usp %x / child_usp %x / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, parent_usp, child_usp, cycle );
+#endif
+
+    // save current values of CPU registers to local CPU context
     hal_do_cpu_save( &context );
 
-    // From this point, both parent and child threads execute the following code.
-    // They can be distinguished by the CURRENT_THREAD value, and child will only 
-    // execute it when it is unblocked by parent, after return to sys_fork().
-    // - parent thread copies user stack, and patch sp_29 / c0_th / C0_sr / c2_ptpr
-    // - child thread does nothing
-
-    thread_t * current = CURRENT_THREAD;
-
-    if( current == parent_ptr )    // current == parent thread
+    // From this point, both parent and child can execute the following code,
+    // but child thread will only execute it after being unblocked by parent thread.
+    // They can be distinguished by the (CURRENT_THREAD,local_cxy) values,
+    // and we must re-initialise the calling thread pointer from c0_th register
+
+    this = CURRENT_THREAD;
+
+    if( (this == parent_ptr) && (local_cxy == parent_cxy) )   // parent thread
     {
-        // get parent and child stack pointers
-        char * parent_sp = (char *)context.sp_29;
-        char * child_sp  = (char *)((intptr_t)parent_sp +
-                                    (intptr_t)child_ptr - 
-                                    (intptr_t)parent_ptr );
- 
-        // patch kernel_stack pointer, current thread, and status slots
-        context.sp_29   = (uint32_t)child_sp;
+        // patch 4 slots in the local CPU context: the sp_29 / c0_th / C0_sr / c2_ptpr
+        // slots are not identical in parent and child
+        context.sp_29   = context.sp_29 + (intptr_t)child_ptr - (intptr_t)parent_ptr;
         context.c0_th   = (uint32_t)child_ptr;
         context.c0_sr   = SR_SYS_MODE;
         context.c2_ptpr = pt_ppn >> 1;
 
-        // copy local context to remote child context)
+        // copy this patched context to remote child context
         hal_remote_memcpy( XPTR( child_cxy , child_context_ptr ),
                            XPTR( local_cxy  , &context ) , 
                            sizeof( hal_cpu_context_t ) );
-
-        // copy kernel stack content from local parent thread to remote child thread
-        uint32_t size = (uint32_t)parent_ptr + CONFIG_THREAD_DESC_SIZE - (uint32_t)parent_sp;
-        hal_remote_memcpy( XPTR( child_cxy , child_sp ),
-                           XPTR( local_cxy , parent_sp ),
-                           size );
+#if DEBUG_HAL_CONTEXT
+cycle = (uint32_t)hal_get_cycles();
+printk("\n[%s] thread[%x,%x] copied CPU context to child / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, cycle );
+#endif
+
+        // parent thread unblock child thread
+        thread_unblock( XPTR( child_cxy , child_ptr ) , THREAD_BLOCKED_GLOBAL );
+
+#if DEBUG_HAL_CONTEXT
+cycle = (uint32_t)hal_get_cycles();
+printk("\n[%s] thread[%x,%x] unblocked child thread / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, cycle );
+#endif
+
     }
-    else                           // current == child thread
-    {
-        assert( (current == child_ptr) , "current = %x / child = %x\n");
-    }
+
 }  // end hal_cpu_context_fork()
 
@@ -285,9 +340,15 @@
 void hal_cpu_context_destroy( thread_t * thread )
 {
-    kmem_req_t  req;
-
-    req.type = KMEM_CPU_CTX;
-    req.ptr  = thread->cpu_context;
-    kmem_free( &req );
+    kmem_req_t          req;
+
+    hal_cpu_context_t * ctx = thread->cpu_context;
+
+    // release CPU context if required
+    if( ctx != NULL )
+    {    
+        req.type = KMEM_CPU_CTX;
+        req.ptr  = ctx;
+        kmem_free( &req );
+    }
 
 }  // end hal_cpu_context_destroy()
@@ -348,7 +409,13 @@
     kmem_req_t  req;
 
-    req.type = KMEM_FPU_CTX;
-    req.ptr  = thread->fpu_context;
-    kmem_free( &req );
+    hal_fpu_context_t * context = thread->fpu_context;
+
+    // release FPU context if required
+    if( context != NULL )
+    {    
+        req.type = KMEM_FPU_CTX;
+        req.ptr  = context;
+        kmem_free( &req );
+    }
 
 }  // end hal_fpu_context_destroy()
Index: trunk/hal/tsar_mips32/core/hal_exception.c
===================================================================
--- trunk/hal/tsar_mips32/core/hal_exception.c	(revision 624)
+++ trunk/hal/tsar_mips32/core/hal_exception.c	(revision 625)
@@ -189,5 +189,5 @@
     if( CURRENT_THREAD->type != THREAD_USER )
     {
-        printk("\n[KERNEL PANIC] in %s : illegal thread type %s\n",
+        printk("\n[PANIC] in %s : illegal thread type %s\n",
         __FUNCTION__, thread_type_str(CURRENT_THREAD->type) );
 
@@ -250,5 +250,5 @@
             else if( error == EXCP_USER_ERROR )      // illegal vaddr
             {
-                printk("\n[USER ERROR] in %s : thread[%x,%x] on core[%x,%x] / cycle %d\n"
+                printk("\n[ERROR] in %s : thread[%x,%x] on core[%x,%x] / cycle %d\n"
                 "  %s : epc %x / badvaddr %x / is_ins %d\n",
                 __FUNCTION__, this->process->pid, this->trdid, local_cxy,
@@ -260,5 +260,5 @@
             else  // error == EXCP_KERNEL_PANIC  
             {
-                printk("\n[KERNEL PANIC] in %s : thread[%x,%x] on core[%x,%x] / cycle %d\n"
+                printk("\n[PANIC] in %s : thread[%x,%x] on core[%x,%x] / cycle %d\n"
                 "  %s : epc %x / badvaddr %x / is_ins %d\n",
                 __FUNCTION__, this->process->pid, this->trdid, local_cxy,
@@ -272,5 +272,5 @@
         case MMU_READ_PRIVILEGE_VIOLATION:   // illegal 
         {
-            printk("\n[USER ERROR] in %s : thread[%x,%x] on core[%x,%x] / cycle %d\n"
+            printk("\n[ERROR] in %s : thread[%x,%x] on core[%x,%x] / cycle %d\n"
             "  %s : epc %x / badvaddr %x / is_ins %d\n",
             __FUNCTION__, this->process->pid, this->trdid, local_cxy,
@@ -299,5 +299,5 @@
             else if( error == EXCP_USER_ERROR )  // illegal write access
             {
-                    printk("\n[USER ERROR] in %s : thread[%x,%x] on core[%x,%x] / cycle %d\n"
+                    printk("\n[ERROR] in %s : thread[%x,%x] on core[%x,%x] / cycle %d\n"
                     "  %s : epc %x / badvaddr %x / is_ins %d\n",
                     __FUNCTION__, this->process->pid, this->trdid, local_cxy,
@@ -309,5 +309,5 @@
             else   // error == EXCP_KERNEL_PANIC
             {
-                printk("\n[KERNEL PANIC] in %s : thread[%x,%x] on core[%x,%x] / cycle %d\n"
+                printk("\n[PANIC] in %s : thread[%x,%x] on core[%x,%x] / cycle %d\n"
                 "  %s : epc %x / badvaddr %x / is_ins %d\n",
                 __FUNCTION__, this->process->pid, this->trdid, local_cxy,
@@ -320,5 +320,5 @@
         case MMU_READ_EXEC_VIOLATION:        // user error
         {
-            printk("\n[USER ERROR] in %s : thread[%x,%x] on core[%x,%x] / cycle %d\n"
+            printk("\n[ERROR] in %s : thread[%x,%x] on core[%x,%x] / cycle %d\n"
             "  %s : epc %x / badvaddr %x / is_ins %d\n",
             __FUNCTION__, this->process->pid, this->trdid, local_cxy,
@@ -330,5 +330,5 @@
         default:                             // this is a kernel error    
         {
-            printk("\n[KERNEL PANIC] in %s : thread[%x,%x] on core[%x,%x] / cycle %d\n"
+            printk("\n[PANIC] in %s : thread[%x,%x] on core[%x,%x] / cycle %d\n"
             "  %s : epc %x / badvaddr %x / is_ins %d\n",
             __FUNCTION__, this->process->pid, this->trdid, local_cxy,
@@ -346,8 +346,6 @@
 //////////////////////////////////////////////////////////////////////////////////////////
 // @ this     : pointer on faulty thread descriptor.
-// @ error    : EXCP_USER_ERROR or EXCP_KERNEL_PANIC
-//////////////////////////////////////////////////////////////////////////////////////////
-static void hal_exception_dump( thread_t * this,
-                                error_t    error )
+//////////////////////////////////////////////////////////////////////////////////////////
+static void hal_exception_dump( thread_t * this )
 {
     core_t    * core    = this->core;
@@ -366,14 +364,6 @@
     remote_busylock_acquire( lock_xp );
 
-    if( error == EXCP_USER_ERROR )
-    {
-        nolock_printk("\n=== USER ERROR / thread(%x,%x) / core[%d] / cycle %d ===\n",
-        process->pid, this->trdid, core->lid, (uint32_t)hal_get_cycles() );
-    }
-    else
-    {
-        nolock_printk("\n=== KERNEL PANIC / thread(%x,%x) / core[%d] / cycle %d ===\n",
-        process->pid, this->trdid, core->lid, (uint32_t)hal_get_cycles() );
-    }
+    nolock_printk("\n=== thread(%x,%x) / core[%d] / cycle %d ===\n",
+    process->pid, this->trdid, core->lid, (uint32_t)hal_get_cycles() );
 
 	nolock_printk("busylocks = %d / blocked_vector = %X / flags = %X\n\n",
@@ -507,5 +497,5 @@
 	if( error == EXCP_USER_ERROR )          //  user error => kill user process
 	{
-        hal_exception_dump( this , error );
+        hal_exception_dump( this );
 
         sys_exit( EXIT_FAILURE );
@@ -513,5 +503,5 @@
     else if( error == EXCP_KERNEL_PANIC )   // kernel error => kernel panic
     {
-        hal_exception_dump( this , error );
+        hal_exception_dump( this );
 
         hal_core_sleep();
Index: trunk/hal/tsar_mips32/core/hal_gpt.c
===================================================================
--- trunk/hal/tsar_mips32/core/hal_gpt.c	(revision 624)
+++ trunk/hal/tsar_mips32/core/hal_gpt.c	(revision 625)
@@ -823,12 +823,16 @@
 ///////////////////////////////////////////
 error_t hal_gpt_pte_copy( gpt_t  * dst_gpt,
+                          vpn_t    dst_vpn,
                           xptr_t   src_gpt_xp,
-                          vpn_t    vpn,
+                          vpn_t    src_vpn,
                           bool_t   cow,
                           ppn_t  * ppn,
                           bool_t * mapped )
 {
-    uint32_t     ix1;       // index in PT1
-    uint32_t     ix2;       // index in PT2
+    uint32_t     src_ix1;   // index in SRC PT1
+    uint32_t     src_ix2;   // index in SRC PT2
+
+    uint32_t     dst_ix1;   // index in DST PT1
+    uint32_t     dst_ix2;   // index in DST PT2
 
     cxy_t        src_cxy;   // SRC GPT cluster
@@ -862,6 +866,6 @@
 thread_t * this  = CURRENT_THREAD;
 if( DEBUG_HAL_GPT_COPY < cycle )
-printk("\n[%s] : thread[%x,%x] enter / vpn %x / src_cxy %x / dst_cxy %x / cycle %d\n", 
-__FUNCTION__, this->process->pid, this->trdid, vpn, src_cxy, local_cxy, cycle );
+printk("\n[%s] : thread[%x,%x] enter / src_cxy %x / dst_cxy %x / cycle %d\n", 
+__FUNCTION__, this->process->pid, this->trdid, src_cxy, local_cxy, cycle );
 #endif
 
@@ -878,9 +882,14 @@
     assert( (dst_pt1 != NULL) , "dst_pt1 does not exist\n");
 
-    ix1 = TSAR_MMU_IX1_FROM_VPN( vpn );
-    ix2 = TSAR_MMU_IX2_FROM_VPN( vpn );
+    // compute SRC indexes
+    src_ix1 = TSAR_MMU_IX1_FROM_VPN( src_vpn );
+    src_ix2 = TSAR_MMU_IX2_FROM_VPN( src_vpn );
+
+    // compute DST indexes
+    dst_ix1 = TSAR_MMU_IX1_FROM_VPN( dst_vpn );
+    dst_ix2 = TSAR_MMU_IX2_FROM_VPN( dst_vpn );
 
     // get src_pte1 
-    src_pte1 = hal_remote_l32( XPTR( src_cxy , &src_pt1[ix1] ) );
+    src_pte1 = hal_remote_l32( XPTR( src_cxy , &src_pt1[src_ix1] ) );
 
     // do nothing if src_pte1 not MAPPED or not SMALL
@@ -888,5 +897,5 @@
     {
         // get dst_pt1 entry
-        dst_pte1 = dst_pt1[ix1];
+        dst_pte1 = dst_pt1[dst_ix1];
 
         // map dst_pte1 if required
@@ -915,5 +924,5 @@
 
             // register it in DST_GPT
-            dst_pt1[ix1] = dst_pte1;
+            dst_pt1[dst_ix1] = dst_pte1;
         }
 
@@ -927,6 +936,6 @@
 
         // get attr and ppn from SRC_PT2
-        src_pte2_attr = hal_remote_l32( XPTR( src_cxy , &src_pt2[2 * ix2]     ) );
-        src_pte2_ppn  = hal_remote_l32( XPTR( src_cxy , &src_pt2[2 * ix2 + 1] ) );
+        src_pte2_attr = hal_remote_l32( XPTR( src_cxy , &src_pt2[2 * src_ix2]     ) );
+        src_pte2_ppn  = hal_remote_l32( XPTR( src_cxy , &src_pt2[2 * src_ix2 + 1] ) );
 
         // do nothing if src_pte2 not MAPPED
@@ -934,14 +943,14 @@
         {
             // set PPN in DST PTE2
-            dst_pt2[2*ix2+1] = src_pte2_ppn;
+            dst_pt2[2 * dst_ix2 + 1] = src_pte2_ppn;
                         
             // set attributes in DST PTE2         
             if( cow && (src_pte2_attr & TSAR_MMU_WRITABLE) ) 
             {
-                dst_pt2[2*ix2] = (src_pte2_attr | TSAR_MMU_COW) & (~TSAR_MMU_WRITABLE);
+                dst_pt2[2 * dst_ix2] = (src_pte2_attr | TSAR_MMU_COW) & (~TSAR_MMU_WRITABLE);
             } 
             else
             {
-                dst_pt2[2*ix2] = src_pte2_attr;
+                dst_pt2[2 * dst_ix2] = src_pte2_attr;
             }
 
@@ -953,6 +962,6 @@
 cycle = (uint32_t)hal_get_cycles;
 if( DEBUG_HAL_GPT_COPY < cycle )
-printk("\n[%s] : thread[%x,%x] exit / copy done for vpn %x / cycle %d\n", 
-__FUNCTION__, this->process->pid, this->trdid, vpn, cycle );
+printk("\n[%s] : thread[%x,%x] exit / copy done for src_vpn %x / dst_vpn %x / cycle %d\n", 
+__FUNCTION__, this->process->pid, this->trdid, src_vpn, dst_vpn, cycle );
 #endif
 
@@ -970,6 +979,6 @@
 cycle = (uint32_t)hal_get_cycles;
 if( DEBUG_HAL_GPT_COPY < cycle )
-printk("\n[%s] : thread[%x,%x] exit / nothing done for vpn %x / cycle %d\n", 
-__FUNCTION__, this->process->pid, this->trdid, vpn, cycle );
+printk("\n[%s] : thread[%x,%x] exit / nothing done / cycle %d\n", 
+__FUNCTION__, this->process->pid, this->trdid, cycle );
 #endif
 
Index: trunk/hal/tsar_mips32/core/hal_kentry.S
===================================================================
--- trunk/hal/tsar_mips32/core/hal_kentry.S	(revision 624)
+++ trunk/hal/tsar_mips32/core/hal_kentry.S	(revision 625)
@@ -4,5 +4,5 @@
  * AUthors   Ghassan Almaless (2007,2008,2009,2010,2011,2012)
  *           Mohamed Lamine Karaoui (2015)
- *           Alain Greiner (2017)
+ *           Alain Greiner (2016,2017,2018,2019)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -87,6 +87,6 @@
 #------------------------------------------------------------------------------------
 # Kernel Entry point for Interrupt / Exception / Syscall
-# The c2_dext and c2_iext CP2 registers must have been previously set
-# to "local_cxy", because the kernel run with MMU desactivated.
+# The c2_dext CP2 register must have been previously set
+# to "local_cxy", because the kernel run with data MMU desactivated.
 #------------------------------------------------------------------------------------
 
@@ -96,5 +96,5 @@
 	andi    $26,    $26,  0x10          # test User Mode bit
 	beq     $26,    $0,	  kernel_mode   # jump if core already in kernel
-	ori     $27,    $0,	  0x3           # $27 <= code for MMU OFF
+	ori     $27,    $0,	  0xB           # $27 <= code data MMU OFF
 	
 #------------------------------------------------------------------------------------
@@ -102,12 +102,12 @@
 # to handle a syscall, an interrupt, or an user exception.
 # - save current c2_mode in $26.
-# - set MMU OFF.
+# - set data MMU OFF.
 # - copy user stack pointer in $27 to be saved in uzone.
-# - set kernel stack pointer in $29 == top_kernel_stack(this).
+# - set kernel stack pointer in $29 (kernel stack empty at firts entry).
 
 user_mode:
 
     mfc2    $26,    $1                  # $26 <= c2_mode
-	mtc2    $27,    $1			        # set MMU OFF
+	mtc2    $27,    $1			        # set data MMU OFF
     move    $27,    $29                 # $27 <= user stack pointer
 	mfc0    $29,    $4,   2             # get pointer on thread descriptor from c0_th
@@ -121,5 +121,5 @@
 # after a syscall, to handle an interrupt, or to handle a non-fatal exception.
 # - save current c2_mode in $26.
-# - set MMU OFF.
+# - set data MMU OFF.
 # - copy current kernel stack pointer in $27.
 
@@ -127,5 +127,5 @@
 
     mfc2    $26,    $1                  # $26 <= c2_mode
-	mtc2    $27,    $1			        # set MMU OFF
+	mtc2    $27,    $1			        # set data MMU OFF
     move    $27,    $29                 # $27 <= current kernel stack pointer
 
@@ -133,5 +133,5 @@
 # This code is executed in both modes (user or kernel):
 # The assumptions are:
-# - c2_mode contains the MMU OFF value.
+# - c2_mode contains the data MMU OFF value.
 # - $26 contains the previous c2_mode value.
 # - $27 contains the previous sp value (can be usp or ksp).
@@ -139,6 +139,6 @@
 # We execute the following actions:
 # - decrement $29 to allocate an uzone in kernel stack
-# - save relevant GPR, CP0 and CP2 registers to uzone.
-# - set the SR in kernel mode: IRQ disabled, clear exl.
+# - save GPR, CP0 and CP2 registers to uzone.
+# - set the SR in kernel mode: IRQ disabled, clear EXL.
 
 unified_mode:
@@ -195,5 +195,5 @@
     sw      $26,    (UZ_MODE*4)($29)    # save previous c2_mode (can be user or kernel)
 
-    mfc0    $3,     $12 
+    mfc0    $3,     $12                 # $3 <= c0_sr
 	srl	    $3,     $3,   5
 	sll     $3,	    $3,   5	            # reset 5 LSB bits
@@ -216,4 +216,14 @@
     nop
     move    $4,     $2
+    jal     putd
+    nop
+    la      $4,     msg_crlf
+    jal     puts
+    nop    
+    # display saved CR value
+    la      $4,     msg_cr
+    jal     puts
+    nop
+    lw	    $4,  	(UZ_CR*4)($29)
     jal     putx
     nop
@@ -286,5 +296,5 @@
     
 #------------------------------------------------------------------------------------
-# This code handle the uzone pointers stack, and calls the relevant 
+# This code handle the two-slots uzone pointers stack, and calls the relevant 
 # Interrupt / Exception / Syscall handler, depending on XCODE in CP0_CR.
 # Both the hal_do_syscall() and the hal_do_exception() functions use
@@ -338,6 +348,6 @@
 # - All registers saved in the uzone are restored, using the pointer on uzone,
 #   that is contained in $29.
-# - The "uzone" field in thread descriptor, that has beeen modified at kernel entry
-#   is restored from value contained in the uzone[UZ_SP] slot. 
+# - The "current_uzone" pointer in thread descriptor, that has beeen modified at
+#   kernel entry is restored from value contained in the uzone[UZ_SP] slot. 
 # -----------------------------------------------------------------------------------
 
@@ -365,4 +375,14 @@
     nop
     move    $4,     $2
+    jal     putd
+    nop
+    la      $4,     msg_crlf
+    jal     puts
+    nop    
+    # display saved CR value
+    la      $4,     msg_cr
+    jal     puts
+    nop
+    lw	    $4,  	(UZ_CR*4)($29)
     jal     putx
     nop
@@ -479,5 +499,5 @@
 
 	lw	    $26,    (UZ_MODE*4)($27)    
-    mtc2    $26,    $1                  # restore CP2_MODE from uzone
+    mtc2    $26,    $1                  # restore c2_mode from uzone
 
 # -----------------------------------------------------------------------------------
@@ -494,4 +514,7 @@
     .section .kdata
 
+msg_cr:
+    .align 2
+    .asciiz "- UZ_CR   = " 
 msg_sp:
     .align 2
Index: trunk/hal/tsar_mips32/core/hal_kentry.h
===================================================================
--- trunk/hal/tsar_mips32/core/hal_kentry.h	(revision 624)
+++ trunk/hal/tsar_mips32/core/hal_kentry.h	(revision 625)
@@ -1,15 +1,16 @@
 /*
- * hal_kentry.h - MIPS32 registers mnemonics
+ * hal_kentry.h - uzone definition
  * 
- * Copyright (c) 2008,2009,2010,2011,2012 Ghassan Almaless
- * Copyright (c) 2011,2012 UPMC Sorbonne Universites
+ * Author     Alain Greiner (2016,2017,2018,2019)
+ *
+ * Copyright (c) UPMC Sorbonne Universites
  * 
- * This file is part of ALMOS-kernel.
+ * This file is part of ALMOS-MKH.
  *
- * ALMOS-kernel is free software; you can redistribute it and/or modify it
+ * 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-kernel is distributed in the hope that it will be useful, but
+ * 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
@@ -28,5 +29,4 @@
 // a fixed size array of 32 bits integers, used by the kentry function to save/restore 
 // the MIPS32 CPU registers, at each exception / interruption / syscall.
-// It also defines several initial values for the SR register.
 //
 // This file is included in the hal_kentry.S, hal_syscall.c, hal_exception.c,
@@ -36,6 +36,9 @@
 
 /****************************************************************************************
- * This structure defines the cpu_uzone dynamically allocated in the kernel stack
- * by the hal_kentry assembly code for the TSAR_MIPS32 architecture.
+ * This structure defines the "uzone" dynamically allocated in the kernel stack
+ * by the hal_kentry assembly code to save the MIPS32 registers each time a core
+ * enters the kernel to handle an interrupt, exception, or syscall.
+ * These define are specific for the TSAR_MIPS32 architecture.
+ *
  * WARNING : It is replicated in hal_kentry.S file.
  ***************************************************************************************/
@@ -87,7 +90,6 @@
  * The hal_kentry_enter() function is the unique kernel entry point in case of 
  * exception, interrupt, or syscall for the TSAR_MIPS32 architecture.  
- * It can be executed by a core in user mode (in case of exception or syscall),
- * or by a core already in kernel mode (in case of interrupt).
- *
+ * It can be executed by a core in user mode or by a core already in kernel mode
+ * (in case of interrupt or non fatal exception).
  * In both cases it allocates an "uzone" space in the kernel stack to save the
  * CPU registers values, desactivates the MMU, and calls the relevant handler 
@@ -95,5 +97,5 @@
  *
  * After handler execution, it restores the CPU context from the uzone and jumps 
- * to address contained in EPC calling hal_kentry_eret()
+ * to address contained in EPC calling the hal_kentry_eret() function.
  ************************************************************************************/
 void hal_kentry_enter( void );
@@ -101,5 +103,5 @@
 /*************************************************************************************
  * The hal_kentry_eret() function contains only the assembly "eret" instruction,
- * that reset the EXL bit in the c0_sr register, and jump to the address 
+ * that reset the EXL bit in the c0_sr register, and jumps to the address 
  * contained in the c0_epc register. 
  * ************************************************************************************/
Index: trunk/hal/tsar_mips32/core/hal_remote.c
===================================================================
--- trunk/hal/tsar_mips32/core/hal_remote.c	(revision 624)
+++ trunk/hal/tsar_mips32/core/hal_remote.c	(revision 625)
@@ -381,9 +381,4 @@
     uint32_t scxy = (uint32_t)GET_CXY( src );
 
-/*
-if( local_cxy == 1 )
-printk("\n@@@ %s : scxy %x / sptr %x / dcxy %x / dptr %x\n",
-__FUNCTION__, scxy, sptr, dcxy, dptr );
-*/
     hal_disable_irq( &save_sr );
 
Index: trunk/hal/tsar_mips32/core/hal_special.c
===================================================================
--- trunk/hal/tsar_mips32/core/hal_special.c	(revision 624)
+++ trunk/hal/tsar_mips32/core/hal_special.c	(revision 625)
@@ -40,4 +40,15 @@
 extern cxy_t local_cxy;
 extern void  hal_kentry_enter( void );
+
+////////////////////////////////////////////////////////////////////////////////
+// For the TSAR architecture, this function registers the address of the
+// hal_kentry_enter() function in the MIPS32 cp0_ebase register.
+////////////////////////////////////////////////////////////////////////////////
+void hal_set_kentry( void )
+{
+    uint32_t kentry = (uint32_t)(&hal_kentry_enter);
+
+    asm volatile("mtc0   %0,  $15,  1" : : "r" (kentry) );
+}
 
 /////////////////////////////////////////////////////////////////////////////////
@@ -48,10 +59,9 @@
 void hal_mmu_init( gpt_t * gpt )
 {
-
-    // set PT1 base address in mmu_ptpr register
+    // set PT1 base address in cp2_ptpr register
     uint32_t ptpr = (((uint32_t)gpt->ptr) >> 13) | (local_cxy << 19);
     asm volatile ( "mtc2   %0,   $0         \n" : : "r" (ptpr) );
 
-    // set ITLB | ICACHE | DCACHE bits in mmu_mode register 
+    // set ITLB | ICACHE | DCACHE bits in cp2_mode register 
     asm volatile ( "ori    $26,  $0,  0xB   \n" 
                    "mtc2   $26,  $1         \n" );
@@ -59,15 +69,20 @@
 
 ////////////////////////////////////////////////////////////////////////////////
-// For the TSAR architecture, this function registers the address of the
-// hal_kentry_enter() function in the MIPS32 cp0_ebase register.
-////////////////////////////////////////////////////////////////////////////////
-void hal_set_kentry( void )
-{
-    uint32_t kentry = (uint32_t)(&hal_kentry_enter);
-
-    asm volatile("mtc0   %0,  $15,  1" : : "r" (kentry) );
-}
-
-////////////////////////////////
+// For the TSAR architecture, this function returns the current value 
+// of the 32 bits c0_sr register
+////////////////////////////////////////////////////////////////////////////////
+inline reg_t hal_get_sr( void )
+{
+    reg_t sr;
+
+	asm volatile ("mfc0    %0,    $12" : "=&r" (sr));
+
+	return sr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// For the TSAR architecture, this function returns the 10 LSB bits 
+// of the 32 bits c0_ebase register : Y (4 bits) | Y (4 bits) | LID (2 bits)
+////////////////////////////////////////////////////////////////////////////////
 inline gid_t hal_get_gid( void )
 {
@@ -79,5 +94,8 @@
 }
 
-///////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+// For the TSAR architecture, this function returns the current value
+// of the 32 bits c0_count cycle counter.
+////////////////////////////////////////////////////////////////////////////////
 inline reg_t hal_time_stamp( void )
 {
@@ -87,14 +105,4 @@
 
     return count;
-}
-
-///////////////////////////////
-inline reg_t hal_get_sr( void )
-{
-    reg_t sr;
-
-	asm volatile ("mfc0    %0,    $12" : "=&r" (sr));
-
-	return sr;
 }
 
@@ -131,5 +139,8 @@
 }
 
-///////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+// For the TSAR architecture, this function returns the current value
+// of the 32 bits c0_th register.
+////////////////////////////////////////////////////////////////////////////////
 inline struct thread_s * hal_get_current_thread( void )
 {
@@ -141,5 +152,8 @@
 }
 
-///////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+// For the TSAR architecture, this function set a new value
+// to the 32 bits c0_th register.
+////////////////////////////////////////////////////////////////////////////////
 void hal_set_current_thread( struct thread_s * thread )
 { 
@@ -182,6 +196,9 @@
 }
 
-///////////////////////////
-uint32_t hal_get_sp( void )
+////////////////////////////////////////////////////////////////////////////////
+// For the TSAR architecture, this function returns the current value
+// of the 32 bits sp_29 register.
+////////////////////////////////////////////////////////////////////////////////
+reg_t hal_get_sp( void )
 {
 	register uint32_t sp;
@@ -190,27 +207,4 @@
   
 	return sp;
-}
-
-/////////////////////////////////////
-uint32_t hal_set_sp( void * new_val )
-{
-	register uint32_t sp;
-  
-	asm volatile
-	( "or    %0,   $0,      $29   \n"
-	  "or    $29,  $0,      %1    \n"
-	  : "=&r" (sp) : "r" (new_val)  );
-  
-	return sp;
-}
-
-///////////////////////////
-uint32_t hal_get_ra( void )
-{
-	register uint32_t ra;
-  
-	asm volatile ("or    %0,   $0,   $31" : "=&r" (ra));
-  
-	return ra;
 }
 
Index: trunk/hal/tsar_mips32/core/hal_switch.S
===================================================================
--- trunk/hal/tsar_mips32/core/hal_switch.S	(revision 624)
+++ trunk/hal/tsar_mips32/core/hal_switch.S	(revision 625)
@@ -2,5 +2,5 @@
  * hal_witch.S - CPU context switch function for TSAR-MIPS32
  * 
- * Author  Alain Greiner    (2016)
+ * Author  Alain Greiner    (2016,2017,2018,2019)
  *
  * Copyright (c)  UPMC Sorbonne Universites
Index: trunk/hal/tsar_mips32/core/hal_syscall.c
===================================================================
--- trunk/hal/tsar_mips32/core/hal_syscall.c	(revision 624)
+++ trunk/hal/tsar_mips32/core/hal_syscall.c	(revision 625)
@@ -29,5 +29,5 @@
 #include <hal_kentry.h>
 
-/////////////////////
+///////////////////////////
 void hal_do_syscall( void )
 {
@@ -63,6 +63,6 @@
                          service_num );
 
-    // get pointer on exit_thread uzone, because
-    // exit_thread can be different from enter_thread
+    // get pointer on exit_thread uzone, because exit thread
+    // can be different from enter_thread for a fork syscall
     this       = CURRENT_THREAD;
     exit_uzone = (uint32_t *)this->uzone_current;
Index: trunk/hal/tsar_mips32/core/hal_uspace.c
===================================================================
--- trunk/hal/tsar_mips32/core/hal_uspace.c	(revision 624)
+++ trunk/hal/tsar_mips32/core/hal_uspace.c	(revision 625)
@@ -249,9 +249,9 @@
         ".set noreorder             \n"
         "move   $13,   %1           \n"   /* $13 <= str                     */
-        "mfc2   $15,   $1           \n"   /* $15 <= DTLB and ITLB off       */
+        "mfc2   $15,   $1           \n"   /* $15 <= MMU_MODE (DTLB off)     */
         "ori    $14,   $15,  0x4    \n"   /* $14 <= mode DTLB on            */
         "1:                         \n"
         "mtc2   $14,   $1			\n"   /* set DTLB on                    */
-        "lb	    $12,   0($13)       \n"   /* read char from user space      */
+        "lb	    $12,   0($13)       \n"   /* $12 <= one byte from u_space   */
         "mtc2   $15,   $1			\n"   /* set DTLB off                   */
         "addi   $13,   $13,  1      \n"   /* increment address              */
Index: trunk/hal/tsar_mips32/core/hal_vmm.c
===================================================================
--- trunk/hal/tsar_mips32/core/hal_vmm.c	(revision 624)
+++ trunk/hal/tsar_mips32/core/hal_vmm.c	(revision 625)
@@ -48,6 +48,6 @@
 // This function is called by the process_zero_init() function during kernel_init.
 // It initializes the VMM of the kernel proces_zero (containing all kernel threads)
-// in the local cluster: it registers one "kcode" vseg in kernel VSL, and registers 
-// one big page in slot[0] of kernel GPT.
+// in the local cluster: For TSAR, it registers one "kcode" vseg in kernel VSL, 
+// and registers one big page in slot[0] of kernel GPT.
 //////////////////////////////////////////////////////////////////////////////////////////
 error_t  hal_vmm_kernel_init( boot_info_t * info )
@@ -119,30 +119,30 @@
     return 0;
 
-}  // end hal_kernel_vmm_init()
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// This function is called by the vmm_init() function to update the VMM of an user
-// process identified by the <process> argument.
-// It registers in the user VSL the "kcode" vseg, registered in the local kernel VSL,
-// and register in the user GPT the big page[0] mapped in the local kernel GPT. 
+}  // end hal_vmm_kernel_init()
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// This function registers in the VMM of an user process identified by the <process> 
+// argument all required kernel vsegs.
+// For TSAR, it registers in the user VSL the "kcode" vseg, from the local kernel VSL,
+// and register in the user GPT the big page[0] from the local kernel GPT. 
 //////////////////////////////////////////////////////////////////////////////////////////
 error_t hal_vmm_kernel_update( process_t * process )
 {
-    error_t error;
+    error_t  error;
     uint32_t attr;
     uint32_t ppn;
 
+    // get cluster identifier
+    cxy_t cxy = local_cxy;
+
 #if DEBUG_HAL_VMM
 thread_t * this = CURRENT_THREAD;
 printk("\n[%s] thread[%x,%x] enter in cluster %x \n", 
-__FUNCTION__, this->process->pid, this->trdid, local_cxy );
+__FUNCTION__, this->process->pid, this->trdid, cxy );
+hal_vmm_display( &process_zero , true );
 hal_vmm_display( process , true );
-hal_vmm_display( &process_zero , true );
-#endif
-
-    // get cluster identifier
-    cxy_t cxy = local_cxy;
-
-    // get extended pointer on kernel GPT
+#endif
+
+    // get extended pointer on local kernel GPT
     xptr_t k_gpt_xp = XPTR( cxy , &process_zero.vmm.gpt );
 
@@ -212,6 +212,6 @@
                       bool_t      mapping )
 {
+    // get pointer on process VMM
     vmm_t * vmm = &process->vmm;
-    gpt_t * gpt = &vmm->gpt;
 
     // get pointers on TXT0 chdev
@@ -220,59 +220,68 @@
     chdev_t * txt0_ptr = GET_PTR( txt0_xp );
 
-    // get extended pointer on remote TXT0 lock
-    xptr_t  lock_xp = XPTR( txt0_cxy , &txt0_ptr->wait_lock );
-
-    // get locks 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 ) );
-
-    // get TXT0 lock
-    remote_busylock_acquire( lock_xp );
-
-    nolock_printk("\n***** VSL and GPT for process %x in cluster %x\n",
-    process->pid , local_cxy );
-
-    // scan the list of vsegs
-    xptr_t         root_xp = XPTR( local_cxy , &vmm->vsegs_root );
-    xptr_t         iter_xp;
-    xptr_t         vseg_xp;
-    vseg_t       * vseg;
-    XLIST_FOREACH( root_xp , iter_xp )
-    {
-        vseg_xp = XLIST_ELEMENT( iter_xp , vseg_t , xlist );
-        vseg    = GET_PTR( vseg_xp );
-
-        nolock_printk(" - %s : base = %X / size = %X / npages = %d\n",
-        vseg_type_str( vseg->type ) , vseg->min , vseg->max - vseg->min , vseg->vpn_size );
-
-        if( mapping )
+    // build extended pointers on TXT0 lock, GPT lock and VSL lock
+    xptr_t  txt_lock_xp = XPTR( txt0_cxy  , &txt0_ptr->wait_lock );
+    xptr_t  vsl_lock_xp = XPTR( local_cxy , &vmm->vsl_lock );
+    xptr_t  gpt_lock_xp = XPTR( local_cxy , &vmm->gpt_lock );
+
+    // get root of vsegs list
+    xptr_t root_xp = XPTR( local_cxy , &vmm->vsegs_root );
+
+    // get the locks protecting TXT0, VSL, and GPT
+    remote_rwlock_rd_acquire( vsl_lock_xp );
+    remote_rwlock_rd_acquire( gpt_lock_xp );
+    remote_busylock_acquire( txt_lock_xp );
+
+    nolock_printk("\n***** VSL and GPT for process %x in cluster %x / PT1 = %x\n",
+    process->pid , local_cxy , vmm->gpt.ptr );
+
+    if( xlist_is_empty( root_xp ) )
+    {
+        nolock_printk("   ... no vsegs registered\n");
+    }
+    else  // scan the list of vsegs
+    {
+        xptr_t         iter_xp;
+        xptr_t         vseg_xp;
+        vseg_t       * vseg;
+
+        XLIST_FOREACH( root_xp , iter_xp )
         {
-            vpn_t    vpn     = vseg->vpn_base;
-            vpn_t    vpn_max = vpn + vseg->vpn_size;
-            ppn_t    ppn;
-            uint32_t attr;
-
-            while( vpn < vpn_max )
+            vseg_xp = XLIST_ELEMENT( iter_xp , vseg_t , xlist );
+            vseg    = GET_PTR( vseg_xp );
+
+            nolock_printk(" - %s : base = %X / size = %X / npages = %d\n",
+            vseg_type_str(vseg->type), vseg->min, vseg->max - vseg->min, vseg->vpn_size );
+
+            if( mapping )  
             {
-                hal_gpt_get_pte( XPTR( local_cxy , gpt ) , vpn , &attr , &ppn );
-
-                if( attr & GPT_MAPPED )
+                vpn_t    vpn     = vseg->vpn_base;
+                vpn_t    vpn_max = vpn + vseg->vpn_size;
+                ppn_t    ppn;
+                uint32_t attr;
+
+                while( vpn < vpn_max )   // scan the PTEs
                 {
-                    if( attr & GPT_SMALL )
+                    hal_gpt_get_pte( XPTR( local_cxy , &vmm->gpt ) , vpn , &attr , &ppn );
+
+                    if( attr & GPT_MAPPED )
                     {
-                        nolock_printk("    . SMALL : vpn = %X / attr = %X / ppn = %X\n",
-                        vpn , attr , ppn );
-                        vpn++;
+                        if( attr & GPT_SMALL )
+                        {
+                            nolock_printk("    . SMALL : vpn = %X / attr = %X / ppn = %X\n",
+                            vpn , attr , ppn );
+                            vpn++;
+                        }
+                        else
+                        {
+                            nolock_printk("    . BIG   : vpn = %X / attr = %X / ppn = %X\n",
+                            vpn , attr , ppn );
+                            vpn += 512;
+                        }
                     }
                     else
                     {
-                        nolock_printk("    . BIG   : vpn = %X / attr = %X / ppn = %X\n",
-                        vpn , attr , ppn );
-                        vpn += 512;
+                        vpn++;
                     }
-                }
-                else
-                {
-                    vpn++;
                 }
             }
@@ -280,10 +289,8 @@
     }
 
-    // release TXT0 lock
-    remote_busylock_release( lock_xp );
-
-    // release the VSK and GPT locks
-    remote_rwlock_rd_release( XPTR( local_cxy , &vmm->vsegs_lock ) );
-    remote_rwlock_rd_release( XPTR( local_cxy , &vmm->gpt_lock ) );
+    // release locks
+    remote_busylock_release( txt_lock_xp );
+    remote_rwlock_rd_release( gpt_lock_xp );
+    remote_rwlock_rd_release( vsl_lock_xp );
 
 }  // hal_vmm_display()
