Index: /soft/giet_vm/Makefile
===================================================================
--- /soft/giet_vm/Makefile	(revision 165)
+++ /soft/giet_vm/Makefile	(revision 166)
@@ -23,4 +23,5 @@
 
 SYS_OBJS_LIST = \
+        vm_handler.o \
 		sys_handler.o \
 		giet.o \
Index: /soft/giet_vm/giet_config.h
===================================================================
--- /soft/giet_vm/giet_config.h	(revision 165)
+++ /soft/giet_vm/giet_config.h	(revision 166)
@@ -35,4 +35,6 @@
 #define GIET_NB_PT2_MAX  	16	    /* max number of level 2 page tables per vspace */
 #define GIET_TICK_VALUE	    65536   /* context switch period (number of cycles) */
+#define GIET_IOMMU_ACTIVE   0		/* The IOMMU vspace is defined */
+#define GIET_IOMMU_CHANNELS 1		/* number of 2Mbytes segments in IOMMU vspace */
+#endif
 
-#endif
Index: /soft/giet_vm/sys/common.c
===================================================================
--- /soft/giet_vm/sys/common.c	(revision 165)
+++ /soft/giet_vm/sys/common.c	(revision 166)
@@ -112,12 +112,10 @@
     unsigned int line_size;
 
-    /*
-     * compute data cache line size based on config register (bits 12:10)
-     */
+    // compute data cache line size based on config register (bits 12:10)
     asm volatile("mfc0 %0, $16, 1" : "=r"(tmp));
     tmp = ((tmp>>10) & 0x7);
     line_size = 2 << tmp;
 
-    /* iterate on cache lines to invalidate each one of them */
+    // iterate on cache lines 
     for (i = 0; i < size; i += line_size)
     {
@@ -160,4 +158,14 @@
         val /= 16;
     }
+}
+///////////////////////////////////////////////////////////////////////////////////
+// 	_get_ptpr()
+// Access CP2 and returns PTPR register.
+///////////////////////////////////////////////////////////////////////////////////
+inline unsigned int _get_ptpr()
+{
+    unsigned int ret;
+    asm volatile("mfc2 %0, $0" : "=r"(ret));
+    return ret;
 }
 ///////////////////////////////////////////////////////////////////////////////////
Index: /soft/giet_vm/sys/common.h
===================================================================
--- /soft/giet_vm/sys/common.h	(revision 165)
+++ /soft/giet_vm/sys/common.h	(revision 166)
@@ -17,4 +17,5 @@
 typedef struct _ld_symbol_s _ld_symbol_t;
 
+extern _ld_symbol_t seg_iob_base;
 extern _ld_symbol_t seg_icu_base;
 extern _ld_symbol_t seg_timer_base;
@@ -42,4 +43,5 @@
 
 unsigned int _get_epc();
+unsigned int _get_ptpr();
 unsigned int _get_bar();
 unsigned int _get_cr();
Index: /soft/giet_vm/sys/drivers.c
===================================================================
--- /soft/giet_vm/sys/drivers.c	(revision 165)
+++ /soft/giet_vm/sys/drivers.c	(revision 166)
@@ -34,4 +34,5 @@
 ///////////////////////////////////////////////////////////////////////////////////
 
+#include <vm_handler.h>
 #include <sys_handler.h>
 #include <giet_config.h>
@@ -87,7 +88,9 @@
 in_unckdata volatile unsigned char _dma_busy[NB_DMAS] = { [0 ... NB_DMAS-1] = 0 };
 
-in_unckdata volatile unsigned char _ioc_status;
-in_unckdata volatile unsigned char _ioc_done = 0;
-in_unckdata volatile unsigned int  _ioc_lock = 0;
+in_unckdata volatile unsigned char _ioc_status       = 0;
+in_unckdata volatile unsigned char _ioc_done         = 0;
+in_unckdata unsigned int		   _ioc_lock         = 0;
+in_unckdata unsigned int		   _ioc_iommu_ix1    = 0;
+in_unckdata unsigned int		   _ioc_iommu_npages = 0;
 
 in_unckdata volatile unsigned char _tty_get_buf[NB_TTYS];
@@ -394,6 +397,31 @@
 ////////////////////////////////////////////////////////////////////////////////
 // The VciBlockDevice is a single channel external storage contrÃŽler.
-// The three functions below use the three variables _ioc_lock _ioc_done,  and
-// _ioc_status for synchronisation.
+//
+// The IOMMU can be activated or not:
+// 
+// 1) When the IOMMU is used, a fixed size 2Mbytes vseg is allocated to 
+// the IOC peripheral, in the I/O virtual space, and the user buffer is
+// dynamically remapped in the IOMMU page table. The corresponding entry 
+// in the IOMMU PT1 is defined by the kernel _ioc_iommu_ix1 variable.
+// The number of pages to be unmapped is stored in the _ioc_npages variable.
+// The number of PT2 entries is dynamically computed and stored in the
+// kernel _ioc_iommu_npages variable. It cannot be larger than 512.
+// The user buffer is unmapped by the _ioc_completed() function when 
+// the transfer is completed.
+//
+// 2/ If the IOMMU is not used, we check that  the user buffer is mapped to a
+// contiguous physical buffer (this is generally true because the user space
+// page tables are statically constructed to use contiguous physical memory).
+//
+// Finally, the memory buffer must fulfill the following conditions:
+// - The user buffer must be word aligned, 
+// - The user buffer must be mapped in user address space, 
+// - The user buffer must be writable in case of (to_mem) access,
+// - The total number of physical pages occupied by the user buffer cannot
+//   be larger than 512 pages if the IOMMU is activated,
+// - All physical pages occupied by the user buffer must be contiguous
+//   if the IOMMU is not activated.
+// An error code is returned if these conditions are not verified.
+//
 // As the IOC component can be used by several programs running in parallel,
 // the _ioc_lock variable guaranties exclusive access to the device.  The
@@ -407,5 +435,5 @@
 // variable.
 // The _ioc_completed() function is polling the _ioc_done variable, waiting for
-// tranfer conpletion. When the completion is signaled, the _ioc_completed()
+// transfer completion. When the completion is signaled, the _ioc_completed()
 // function reset the _ioc_done variable to zero, and releases the _ioc_lock
 // variable.
@@ -447,10 +475,176 @@
 
 ///////////////////////////////////////////////////////////////////////////////
-//  _ioc_write()
-//
-// Transfer data from a memory buffer to a file on the block_device. 
-// The source memory buffer must be in user address space.
-// - lba    : first block index on the disk.
-// - buffer : base address of the memory buffer.
+//  _ioc_access()
+// This function transfer data between a memory buffer and the block device.
+// The buffer lentgth is (count*block_size) bytes.
+//
+// Arguments are:
+// - to_mem     : from external storage to memory when non 0
+// - lba        : first block index on the external storage.
+// - user_vaddr : virtual base address of the memory buffer.
+// - count      : number of blocks to be transfered.
+// Returns 0 if success, > 0 if error.
+///////////////////////////////////////////////////////////////////////////////
+unsigned int _ioc_access( unsigned int  to_mem,
+                          unsigned int 	lba,
+                          unsigned int  user_vaddr,
+                          unsigned int 	count )
+{
+    unsigned int	user_vpn_min;
+    unsigned int	user_vpn_max;
+    unsigned int	vpn;			// virtual page number in user space
+    unsigned int	ppn;			// physical page number
+    unsigned int	flags;			// page protection flags
+    unsigned int	ix2;			// Page index (for IOMMU page table)
+    unsigned int	addr;			// buffer address for IOC 
+    page_table_t*	user_ptp;		// user page table pointer
+    unsigned int	ko;				// bool returned by _v2p_translate()
+    unsigned int	ppn_first;		// first physical page number for user buffer
+        
+    // check buffer alignment
+    if ( (unsigned int)user_vaddr & 0x3 ) return 1;
+
+    unsigned int*	ioc_address = (unsigned int*)&seg_ioc_base;
+    unsigned int	block_size   = ioc_address[BLOCK_DEVICE_BLOCK_SIZE];
+    unsigned int	length       = count*block_size;
+
+    // get user space page table base address
+    user_ptp     = (page_table_t*)(_get_ptpr() << 13);
+    
+    user_vpn_min = user_vaddr >> 12;
+    user_vpn_max = (user_vaddr + length - 1) >> 12;
+    ix2          = 0;
+
+    // loop on all virtual pages covering the user buffer
+    for ( vpn = user_vpn_min ; vpn <= user_vpn_max ; vpn++ )
+    {
+        // get ppn and flags for each vpn
+        ko = _v2p_translate( user_ptp,	// user page table pointer
+                             vpn,		// virtual page number
+                             &ppn,		// physical page number
+                             &flags );	// protection flags
+
+        // check access rights
+        if ( ko )								  return 2;		// unmapped
+        if ( (flags & PTE_U) == 0 )				  return 3;		// not in user space
+        if ( ( (flags & PTE_W) == 0 ) && to_mem ) return 4;		// not writable
+
+        // save first ppn value
+        if ( ix2 == 0 ) ppn_first = ppn;
+
+        if ( GIET_IOMMU_ACTIVE )    // the user buffer must be remapped in the I/0 space
+        {
+            // check buffer length < 2 Mbytes
+            if ( ix2 > 511 ) return 2;
+
+            // map the physical page in IOMMU page table
+            _iommu_add_pte2( _ioc_iommu_ix1,	// PT1 index
+                             ix2,				// PT2 index
+					         ppn,				// Physical page number	
+                             flags );			// Protection flags
+
+            // buffer base address for IOC with IOMMU
+        }
+        else			// no IOMMU : check that physical pages are contiguous
+        {
+            if ( (ppn - ppn_first) != ix2 )	      return 5;		// split physical buffer  
+        }
+        
+        // increment page index
+        ix2++;
+    } // end for vpn
+
+    // register the number of pages to be unmapped
+    _ioc_iommu_npages = (user_vpn_max - user_vpn_min) + 1;
+
+    // invalidate data cache in case of memory write
+    if ( to_mem ) _dcache_buf_invalidate( (void*)user_vaddr, length );
+
+    // compute buffer base address for IOC depending on IOMMU activation
+    if ( GIET_IOMMU_ACTIVE ) addr = (_ioc_iommu_ix1) << 21 | (user_vaddr & 0xFFF);
+    else                     addr = ppn_first | (user_vaddr & 0xFFF);
+
+    // get the lock on ioc device 
+    _ioc_get_lock();
+
+    // peripheral configuration  
+    ioc_address[BLOCK_DEVICE_BUFFER]     = addr;
+    ioc_address[BLOCK_DEVICE_COUNT]      = count;
+    ioc_address[BLOCK_DEVICE_LBA]        = lba;
+    if ( to_mem == 0 ) ioc_address[BLOCK_DEVICE_OP] = BLOCK_DEVICE_WRITE;
+    else               ioc_address[BLOCK_DEVICE_OP] = BLOCK_DEVICE_READ;
+
+    return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+// _ioc_completed()
+//
+// This function checks completion of an I/O transfer and reports errors. 
+// As it is a blocking call, the processor is stalled.
+// If the virtual memory is activated, the pages mapped in the I/O virtual
+// space are unmapped, and the IOB TLB is cleared.
+// Returns 0 if success, > 0 if error.
+/////////////////////////////////////////////////////////////////////////////////
+unsigned int _ioc_completed()
+{
+    unsigned int	ret;
+    unsigned int	ix2;
+
+    // busy waiting
+    while (_ioc_done == 0)
+        asm volatile("nop");
+
+    // unmap the buffer from IOMMU page table if IOMMU is activated
+    if ( GIET_IOMMU_ACTIVE )
+    {
+        unsigned int* iob_address = (unsigned int*)&seg_iob_base;
+
+        for ( ix2 = 0 ; ix2 < _ioc_iommu_npages ; ix2++ )
+        {
+            // unmap the page in IOMMU page table
+            _iommu_inval_pte2( _ioc_iommu_ix1,	// PT1 index 
+                              ix2 );			// PT2 index
+
+            // clear IOMMU TLB
+            iob_address[IOB_INVAL_PTE] = (_ioc_iommu_ix1 << 21) | (ix2) << 12; 
+        }
+    }
+
+    // test IOC status 
+    if ((_ioc_status != BLOCK_DEVICE_READ_SUCCESS)
+            && (_ioc_status != BLOCK_DEVICE_WRITE_SUCCESS)) ret = 1;	// error
+    else                                                    ret = 0;	// success
+
+    // reset synchronization variables
+    _ioc_lock =0;
+    _ioc_done =0;
+
+    return ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// _ioc_read()
+// Transfer data from the block device to a memory buffer in user space. 
+// - lba    : first block index on the block device
+// - buffer : base address of the memory buffer (must be word aligned)
+// - count  : number of blocks to be transfered.
+// Returns 0 if success, > 0 if error.
+///////////////////////////////////////////////////////////////////////////////
+unsigned int _ioc_read( unsigned int 	lba, 
+                        void*		    buffer, 
+                        unsigned int	count )
+{
+    return _ioc_access( 1,		// read
+                        lba,
+                        (unsigned int)buffer,
+                        count );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// _ioc_write()
+// Transfer data from a memory buffer in user space to the block device. 
+// - lba    : first block index on the block device
+// - buffer : base address of the memory buffer (must be word aligned)
 // - count  : number of blocks to be transfered.
 // Returns 0 if success, > 0 if error.
@@ -458,98 +652,10 @@
 unsigned int _ioc_write( unsigned int 	lba, 
                          const void*	buffer, 
-                         unsigned int 	count)
-{
-    volatile unsigned int *ioc_address;
-
-    ioc_address = (unsigned int*)&seg_ioc_base;
-
-    /* buffer must be in user space */
-    unsigned int block_size = ioc_address[BLOCK_DEVICE_BLOCK_SIZE];
-
-    if (((unsigned int)buffer >= 0x80000000)
-            || (((unsigned int)buffer + block_size*count) >= 0x80000000))
-        return 1;
-
-    /* get the lock on ioc device */
-    _ioc_get_lock();
-
-    /* block_device configuration for the write transfer */
-    ioc_address[BLOCK_DEVICE_BUFFER] = (unsigned int)buffer;
-    ioc_address[BLOCK_DEVICE_COUNT] = count;
-    ioc_address[BLOCK_DEVICE_LBA] = lba;
-    ioc_address[BLOCK_DEVICE_IRQ_ENABLE] = 1;
-    ioc_address[BLOCK_DEVICE_OP] = BLOCK_DEVICE_WRITE;
-
-    return 0;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// _ioc_read()
-//
-// Transfer data from a file on the block device to a memory buffer. 
-// The destination memory buffer must be in user address space.
-// - lba    : first block index on the disk.
-// - buffer : base address of the memory buffer.
-// - count  : number of blocks to be transfered.
-// All cache lines corresponding to the the target buffer are invalidated
-// for cache coherence.
-// Returns 0 if success, > 0 if error.
-///////////////////////////////////////////////////////////////////////////////
-unsigned int _ioc_read( unsigned int 	lba, 
-                        void*		buffer, 
-                        unsigned int	count )
-{
-    volatile unsigned int *ioc_address;
-
-    ioc_address = (unsigned int*)&seg_ioc_base;
-
-    /* buffer must be in user space */
-    unsigned int block_size = ioc_address[BLOCK_DEVICE_BLOCK_SIZE];
-
-    if (((unsigned int)buffer >= 0x80000000)
-            || (((unsigned int)buffer + block_size*count) >= 0x80000000))
-        return 1;
-
-    /* get the lock on ioc device */
-    _ioc_get_lock();
-
-    /* block_device configuration for the read transfer */
-    ioc_address[BLOCK_DEVICE_BUFFER] = (unsigned int)buffer;
-    ioc_address[BLOCK_DEVICE_COUNT] = count;
-    ioc_address[BLOCK_DEVICE_LBA] = lba;
-    ioc_address[BLOCK_DEVICE_IRQ_ENABLE] = 1;
-    ioc_address[BLOCK_DEVICE_OP] = BLOCK_DEVICE_READ;
-
-    /* invalidation of data cache */
-    _dcache_buf_invalidate(buffer, block_size*count);
-
-    return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////
-// _ioc_completed()
-//
-// This function checks completion of an I/O transfer and reports errors. 
-// As it is a blocking call, the processor is stalled until the next interrupt.
-// Returns 0 if success, > 0 if error.
-/////////////////////////////////////////////////////////////////////////////////
-unsigned int _ioc_completed()
-{
-    unsigned int ret;
-
-    /* busy waiting */
-    while (_ioc_done == 0)
-        asm volatile("nop");
-
-    /* test IOC status */
-    if ((_ioc_status != BLOCK_DEVICE_READ_SUCCESS)
-            && (_ioc_status != BLOCK_DEVICE_WRITE_SUCCESS)) ret = 1;	/* error */
-    else						    ret = 0;	/* success */
-
-    /* reset synchronization variables */
-    _ioc_lock =0;
-    _ioc_done =0;
-
-    return ret;
+                         unsigned int	count )
+{
+    return _ioc_access( 0,		// write
+                        lba,
+                        (unsigned int)buffer,
+                        count );
 }
 
Index: /soft/giet_vm/sys/drivers.h
===================================================================
--- /soft/giet_vm/sys/drivers.h	(revision 165)
+++ /soft/giet_vm/sys/drivers.h	(revision 166)
@@ -18,5 +18,5 @@
 extern volatile unsigned char _ioc_status;
 extern volatile unsigned char _ioc_done;
-extern volatile unsigned int _ioc_lock;
+extern unsigned int           _ioc_lock;
 
 extern volatile unsigned char _tty_get_buf[];
Index: /soft/giet_vm/sys/hwr_mapping.h
===================================================================
--- /soft/giet_vm/sys/hwr_mapping.h	(revision 165)
+++ /soft/giet_vm/sys/hwr_mapping.h	(revision 166)
@@ -86,4 +86,17 @@
 };
 
+/* IOB */
+enum IOB_registers {
+    IOB_IOMMU_PTPR       = 0,	/* R/W : Page Table Pointer Register */
+    IOB_IOMMU_ACTIVE     = 1,   /* R/W : IOMMU activated if not 0 */
+    IOB_IOMMU_BVAR       = 2,	/* R   : Bad Virtual Address (unmapped) */
+    IOB_IOMMU_ETR        = 3,	/* R   : Error Type */
+    IOB_IOMMU_BAD_ID     = 4,	/* R   : Faulty Peripheral Index */
+    IOB_INVAL_PTE        = 5,	/* W   : Invalidate a PTE (virtual address) */
+    IOB_IT_ADDR_IOMMU_LO = 6,	/* W/R : 32 LSB bits for IOMMU IT*/
+    IOB_IT_ADDR_IOMMU_HI = 7,   /* W/R : 32 MSB bits for IOMMU IT */
+    IOB_IT_ADDRESS_BEGIN = 8,   /* R/W : Peripheral IT address (2 32 bits registers) */
+};
+
 #endif
 
Index: /soft/giet_vm/sys/kernel_init.c
===================================================================
--- /soft/giet_vm/sys/kernel_init.c	(revision 165)
+++ /soft/giet_vm/sys/kernel_init.c	(revision 166)
@@ -22,4 +22,5 @@
 #include <mips32_registers.h>
 #include <irq_handler.h>
+#include <vm_handler.h>
 #include <hwr_mapping.h>
 #include <mwmr_channel.h>
@@ -523,10 +524,31 @@
 
 ////////////////////////////////////////////////////////////////////////////////
-// This function intializes the external periherals such as the TTY controller,
-// the IOC (external disk controller), the NIC (external network controller), 
-// the FBDMA (frame buffer controller), etc.
+// This function intializes the external periherals such as the IOB component
+// (I/O bridge, containing the IOMMU, the IOC (external disk controller), 
+// the NIC (external network controller), the FBDMA (frame buffer controller), 
 ////////////////////////////////////////////////////////////////////////////////
 in_kinit void _kernel_peripherals_init()
 {
+    // IOC peripheral initialisation
+    // we simply activate the IOC interrupts...
+    unsigned int*	ioc_address = (unsigned int*)&seg_ioc_base;
+
+    ioc_address[BLOCK_DEVICE_IRQ_ENABLE] = 1;
+    
+    // IOB peripheral
+    if ( GIET_IOMMU_ACTIVE )
+    {
+        unsigned int*	iob_address = (unsigned int*)&seg_iob_base;
+        unsigned int	icu_address = (unsigned int)&seg_icu_base;
+
+        // define IPI address mapping the IOC interrupt ...TODO...
+
+        // set IOMMU page table address
+        iob_address[IOB_IOMMU_PTPR] = (unsigned int)(&_iommu_ptab);    
+
+        // activate IOMMU
+        iob_address[IOB_IOMMU_ACTIVE] = 1;    
+    }
+
     _puts("\n[INIT] Peripherals initialisation completed at cycle ");
     _putw( _proctime() );
@@ -536,6 +558,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
-// This function intialises the centralised interrupt vector,
-// and the ICUs mask registers for all processors in all clusters.
+// This function intialises the interrupt vector, and initialises
+// the ICU mask registers for all processors in all clusters.
 // It strongly depends on the actual peripheral hardware wiring.
 // In this peculiar version, all clusters are identical,
Index: /soft/giet_vm/sys/sys.ld
===================================================================
--- /soft/giet_vm/sys/sys.ld	(revision 165)
+++ /soft/giet_vm/sys/sys.ld	(revision 166)
@@ -22,5 +22,6 @@
 seg_gcd_base      	= 0x95000000;   /* GCD device */
 seg_fb_base       	= 0x96000000;   /* FrameBuffer device */
-seg_icu_base      	= 0x9F000000;   /* ICU device */
+seg_icu_base      	= 0x9F000000;   /* ICU or XICU device */
+seg_iob_base      	= 0x9E000000;   /* IOB device */
 
 /*
