source: soft/giet_vm/giet_drivers/bdv_driver.c @ 289

Last change on this file since 289 was 289, checked in by cfuguet, 10 years ago

Modifications on GIET-VM IOC driver:

  • Introducing new layer on the IOC driver. Every call to ioc_read, ioc_write, ioc_get_block_size or ioc_init

functions will call the specific driver of the used IOC
controller. Supported IOC controllers are (for now) :

  1. BDV (Soclib Block Device)
  2. HBA
  3. SPI (SDCARD - SPI controller)
  • All functions of IOC controllers drivers respect the same interface.
  • To specify the used IOC controller of the platform, a subtype field has been introduced on the map.xml file. This subtype field must be declared on the IOC periph instantiation. Available subtypes (for now) : BDV, HBA or SPI.
File size: 11.4 KB
Line 
1///////////////////////////////////////////////////////////////////////////////////
2// File      : bdv_driver.c
3// Date      : 23/05/2013
4// Author    : alain greiner
5// Maintainer: cesar fuguet
6// Copyright (c) UPMC-LIP6
7///////////////////////////////////////////////////////////////////////////////////
8// The bdv_driver.c and bdv_driver.h files are part ot the GIET-VM kernel.
9// This driver supports the SocLib vci_block_device component, that is
10// a single channel, block oriented, external storage contrÃŽler.
11//
12// It can exist only one block-device controler in the architecture.
13//
14// The _bdv_read() and _bdv_write() functions use the _bdv_access() function,
15// that is always blocking, but can be called in 4 modes:
16//
17// - In BOOT_PA mode, the _bdv_access() function uses a polling policy on the
18//   IOC_STATUS register to detect transfer completion, as hardware interrupts
19//   are not activated. This mode is used by the boot code to load the map.bin
20//   file into memory.
21//
22// - In BOOT_VA mode, the _bdv_access() function uses a polling policy on
23//   IOC_STATUS register to detect transfer completion. This mode is used by
24//   the boot code to load the various .elf files into memory.
25//
26// - In KERNEL mode, the _bdv_access() function uses a descheduling strategy:
27//   The ISR executed when transfer completes should restart the calling task.
28//   There is no checking of user access right to the memory buffer. This mode
29//   must be used to access IOC, for an "open" system call.
30//
31// - In USER mode, the _bdv_access() function uses a descheduling strategy:
32//   The ISR executed when transfer completes should restart the calling task,
33//   The user access right to the memory buffer must be checked.
34//   This mode must be used to access IOC, for a "read/write" system call.
35//
36// As the BDV component can be used by several programs running in parallel,
37// the _ioc_lock variable guaranties exclusive access to the device.  The
38// _bdv_read() and _bdv_write() functions use atomic LL/SC to get the lock.
39//
40// Finally, the memory buffer must fulfill the following conditions:
41// - The buffer must be word aligned,
42// - The buffer must be mapped in user space for an user access,
43// - The buffer must be writable in case of (to_mem) access,
44// - The total number of physical pages occupied by the user buffer cannot
45//   be larger than 512 pages if the IOMMU is activated,
46// - All physical pages occupied by the user buffer must be contiguous
47//   if the IOMMU is not activated.
48// An error code is returned if these conditions are not verified.
49///////////////////////////////////////////////////////////////////////////////////
50// The seg_ioc_base virtual base addresses must be defined in giet_vsegs.ld file.
51///////////////////////////////////////////////////////////////////////////////////
52
53#include <giet_config.h>
54#include <ioc_driver.h>
55#include <bdv_driver.h>
56#include <utils.h>
57#include <tty_driver.h>
58#include <ctx_handler.h>
59
60///////////////////////////////////////////////////////////////////////////////
61//      _bdv_access()
62// This function transfer data between a memory buffer and the block device.
63// The buffer lentgth is (count*block_size) bytes.
64// Arguments are:
65// - to_mem     : from external storage to memory when non 0.
66// - mode       : BOOT / KERNEL / USER
67// - lba        : first block index on the external storage.
68// - buf_paddr  : physical base address of the memory buffer.
69// - count      : number of blocks to be transfered.
70// Returns 0 if success, > 0 if error.
71///////////////////////////////////////////////////////////////////////////////
72static unsigned int _bdv_access( unsigned int to_mem,
73                                 unsigned int mode,
74                                 unsigned int lba,
75                                 paddr_t buf_paddr,
76                                 unsigned int count) 
77{
78
79#if GIET_DEBUG_IOC_DRIVER
80_tty_get_lock( 0 );
81_puts("\n[IOC DEBUG] Enter _bdv_access() at cycle ");
82_putd( _get_proctime() );
83_puts(" for processor ");
84_putd( _get_procid() );
85_puts("\n - mode    = ");
86_putd( mode );
87_puts("\n - paddr   = ");
88_putx( buf_paddr );
89_puts("\n - sectors = ");
90_putd( count );
91_puts("\n - lba     = ");
92_putx( lba );
93_puts("\n");
94_tty_release_lock( 0 );
95#endif
96
97    volatile unsigned int * ioc_address = (unsigned int *) &seg_ioc_base ;
98    unsigned int error = 0;
99
100    // get the lock protecting IOC
101    _get_lock(&_ioc_lock);
102
103    // set the _ioc_status polling variable
104    _ioc_status = BLOCK_DEVICE_BUSY;
105
106    ioc_address[BLOCK_DEVICE_BUFFER]     = (unsigned int)buf_paddr;
107    ioc_address[BLOCK_DEVICE_BUFFER_EXT] = (unsigned int)(buf_paddr>>32);
108    ioc_address[BLOCK_DEVICE_COUNT]      = count;
109    ioc_address[BLOCK_DEVICE_LBA]        = lba;
110
111    // There are two policies for transfer completion
112        // detection, depending on the mode argument:
113
114    if ( (mode == IOC_BOOT_PA_MODE) ||    // We poll directly the IOC_STATUS register
115         (mode == IOC_BOOT_VA_MODE) )     // as IRQs are masked.
116    {
117        // Launch transfert
118        if (to_mem == 0) ioc_address[BLOCK_DEVICE_OP] = BLOCK_DEVICE_WRITE;
119        else             ioc_address[BLOCK_DEVICE_OP] = BLOCK_DEVICE_READ;
120
121        unsigned int status;
122        do
123        {
124            if ( _bdv_get_status( 0, &status ) ) return 1;
125
126#if GIET_DEBUG_IOC_DRIVER
127_tty_get_lock( 0 );
128_puts("\n[IOC DEBUG] _bdv_access() : ... waiting on IOC_STATUS register ...\n");
129_tty_release_lock( 0 );
130#endif
131        }
132        while( (status != BLOCK_DEVICE_READ_SUCCESS)  &&
133               (status != BLOCK_DEVICE_READ_ERROR)    &&
134               (status != BLOCK_DEVICE_WRITE_SUCCESS) &&
135               (status != BLOCK_DEVICE_WRITE_ERROR)   );
136
137        // analyse status
138        error = ( (status == BLOCK_DEVICE_READ_ERROR) ||
139                  (status == BLOCK_DEVICE_WRITE_ERROR) );
140
141        // release lock
142        _release_lock(&_ioc_lock);     
143    }
144    else                           // in USER or KERNEL mode, we deschedule the task.
145                                   // When the task is rescheduled by the ISR, we reset
146                                   // the _ioc_status variable, and release the lock
147    {
148        // We need a critical section, because we must reset the RUN bit
149                // before to launch the transfer, and we want to avoid to be descheduled
150                // between these two operations.
151
152        // Enter critical section
153        _it_disable(); 
154       
155        // set _ioc_gtid and reset runnable
156        unsigned int ltid = _get_proc_task_id();
157        unsigned int pid = _get_procid();
158        _ioc_gtid = (pid<<16) + ltid;
159        _set_task_slot( pid, ltid, CTX_RUN_ID, 0 ); 
160       
161        // Launch transfert
162        if (to_mem == 0) ioc_address[BLOCK_DEVICE_OP] = BLOCK_DEVICE_WRITE;
163        else             ioc_address[BLOCK_DEVICE_OP] = BLOCK_DEVICE_READ;
164
165        // deschedule task
166        _ctx_switch();                     
167
168        // analyse status
169        error = ( (_ioc_status == BLOCK_DEVICE_READ_ERROR) ||
170                  (_ioc_status == BLOCK_DEVICE_WRITE_ERROR) );
171
172        // reset _ioc_status and release lock
173        _ioc_status = BLOCK_DEVICE_IDLE; 
174        _release_lock(&_ioc_lock);     
175    }
176
177#if GIET_DEBUG_IOC_DRIVER
178_tty_get_lock( 0 );
179_puts("\n[IOC DEBUG] _bdv_access completed at cycle ");
180_putd( _get_proctime() );
181_puts(" for processor ");
182_putd( _get_procid() );
183_puts(" : error = ");
184_putd( (unsigned int)error );
185_puts("\n");
186_tty_release_lock( 0 );
187#endif
188
189    return error;
190} // end _bdv_access()
191
192///////////////////////////////////////////////////////////////////////////////
193//       _bdv_init()
194// This function cheks block size, and activates the IOC interrupts.
195// Return 0 for success, > 0 if error
196///////////////////////////////////////////////////////////////////////////////
197unsigned int _bdv_init( unsigned int channel )
198{
199    volatile unsigned int * ioc_address = (unsigned int *) &seg_ioc_base ;
200   
201    if ( ioc_address[BLOCK_DEVICE_BLOCK_SIZE] != 512 )
202    {
203        _tty_get_lock( 0 );
204        _puts("\n[GIET ERROR] in _bdv_init() : block size must be 512 bytes\n");
205        _tty_release_lock( 0 );
206        return 1; 
207    }
208
209    if ( channel != 0 )
210    {
211        _tty_get_lock( 0 );
212        _puts("\n[GIET ERROR] in _bdv_init() : illegal channel\n");
213        _tty_release_lock( 0 );
214
215        return 1;
216    }
217
218    ioc_address[BLOCK_DEVICE_IRQ_ENABLE] = 1;
219    return 0;
220}
221
222///////////////////////////////////////////////////////////////////////////////
223//     _bdv_read()
224// Transfer data from the block device to a memory buffer.
225// - mode     : BOOT / KERNEL / USER
226// - lba      : first block index on the block device
227// - buffer   : base address of the memory buffer (must be word aligned)
228// - count    : number of blocks to be transfered.
229// Returns 0 if success, > 0 if error.
230///////////////////////////////////////////////////////////////////////////////
231unsigned int _bdv_read( unsigned int mode, 
232                        unsigned int lba, 
233                        paddr_t      buffer, 
234                        unsigned int count) 
235{
236    return _bdv_access( 1,        // read access
237                        mode, 
238                        lba,
239                        buffer,
240                        count );
241}
242
243///////////////////////////////////////////////////////////////////////////////
244//     _bdv_write()
245// Transfer data from a memory buffer to the block device.
246// - mode     : BOOT / KERNEL / USER
247// - lba      : first block index on the block device
248// - buffer   : base address of the memory buffer (must be word aligned)
249// - count    : number of blocks to be transfered.
250// Returns 0 if success, > 0 if error.
251///////////////////////////////////////////////////////////////////////////////
252unsigned int _bdv_write( unsigned int mode, 
253                         unsigned int lba, 
254                         paddr_t  buffer, 
255                         unsigned int count ) 
256{
257    return _bdv_access( 0,        // write access
258                        mode, 
259                        lba,
260                        buffer,
261                        count );
262}
263
264///////////////////////////////////////////////////////////////////////////////
265//     _bdv_get_status()
266// This function returns in the status variable, the transfert status, and
267// acknowledge the IRQ if the IOC controler is not busy.
268// Returns 0 if success, > 0 if error
269///////////////////////////////////////////////////////////////////////////////
270unsigned int _bdv_get_status( unsigned int  channel,
271                              unsigned int* status )
272{
273    if ( channel != 0 )
274    {
275        _tty_get_lock( 0 );
276        _puts("\n[GIET ERROR] in _bdv_get_status() : illegal channel\n");
277        _tty_release_lock( 0 );
278
279        return 1;
280    }
281
282    // get IOC base address
283    volatile unsigned int * ioc_address = (unsigned int *) &seg_ioc_base;
284    *status = ioc_address[BLOCK_DEVICE_STATUS];
285
286    return 0;
287}
288
289///////////////////////////////////////////////////////////////////////////////
290//     _bdv_get_block_size()
291// This function returns the block_size with which the IOC has been configured.
292///////////////////////////////////////////////////////////////////////////////
293unsigned int _bdv_get_block_size() 
294{
295    // get IOC base address
296    volatile unsigned int * ioc_address = (unsigned int *) &seg_ioc_base;
297   
298    return  ioc_address[BLOCK_DEVICE_BLOCK_SIZE];
299}
300
301
302// Local Variables:
303// tab-width: 4
304// c-basic-offset: 4
305// c-file-offsets:((innamespace . 0)(inline-open . 0))
306// indent-tabs-mode: nil
307// End:
308// vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
309
Note: See TracBrowser for help on using the repository browser.