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 );
 }
 
