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

Last change on this file since 338 was 333, checked in by alain, 11 years ago

Cosmetic

File size: 14.3 KB
RevLine 
[283]1///////////////////////////////////////////////////////////////////////////////////
[284]2// File      : bdv_driver.c
3// Date      : 23/05/2013
4// Author    : alain greiner
5// Maintainer: cesar fuguet
[283]6// Copyright (c) UPMC-LIP6
7///////////////////////////////////////////////////////////////////////////////////
[284]8// The bdv_driver.c and bdv_driver.h files are part ot the GIET-VM kernel.
[283]9// This driver supports the SocLib vci_block_device component, that is
10// a single channel, block oriented, external storage contrÃŽler.
11//
[295]12// The _bdv_read() and _bdv_write() functions are always blocking.
13// They can be called in 3 modes:
[283]14//
[295]15// - In BOOT mode, these functions use a polling policy on the BDV STATUS
16//   register to detect transfer completion, as interrupts are not activated.
17//   This mode is used by the boot code to load the map.bin file into memory
18//   (before MMU activation), or to load the .elf files (after MMU activation).
[283]19//
[295]20// - In KERNEL mode, these functions use a descheduling strategy:
[284]21//   The ISR executed when transfer completes should restart the calling task.
[295]22//   There is no checking of user access right to the memory buffer.
23//   This mode must be used, for an "open" system call.
[283]24//
[295]25// - In USER mode, these functions use a descheduling strategy:
[283]26//   The ISR executed when transfer completes should restart the calling task,
27//   The user access right to the memory buffer must be checked.
[295]28//   This mode must be used for a "read/write" system call.
[283]29//
[284]30// As the BDV component can be used by several programs running in parallel,
[295]31// the _bdv_lock variable guaranties exclusive access to the device.  The
[283]32// _bdv_read() and _bdv_write() functions use atomic LL/SC to get the lock.
33//
34// Finally, the memory buffer must fulfill the following conditions:
35// - The buffer must be word aligned,
36// - The buffer must be mapped in user space for an user access,
37// - The buffer must be writable in case of (to_mem) access,
38// - The total number of physical pages occupied by the user buffer cannot
39//   be larger than 512 pages if the IOMMU is activated,
40// - All physical pages occupied by the user buffer must be contiguous
41//   if the IOMMU is not activated.
42// An error code is returned if these conditions are not verified.
[295]43//
[320]44// The SEG_IOC_BASE address must be defined in the hard_config.h file.
[283]45///////////////////////////////////////////////////////////////////////////////////
[295]46// Implementation notes:
47//
48// 1. In order to share code, the two _bdv_read() and _bdv_write() functions
49//    call the same _bdv_access() function.
50//
51// 2. All accesses to BDV registers are done by the two
52//    _bdv_set_register() and _bdv_get_register() low-level functions,
53//    that are handling virtual / physical extended addressing.
[283]54///////////////////////////////////////////////////////////////////////////////////
55
56#include <giet_config.h>
[320]57#include <hard_config.h>
[295]58#include <bdv_driver.h>
59#include <xcu_driver.h>
[283]60#include <ioc_driver.h>
61#include <utils.h>
62#include <tty_driver.h>
63#include <ctx_handler.h>
64
65///////////////////////////////////////////////////////////////////////////////
[295]66// BDV global variables
67///////////////////////////////////////////////////////////////////////////////
68
69#define in_unckdata __attribute__((section (".unckdata")))
70
71in_unckdata unsigned int          _bdv_lock = 0;
72in_unckdata volatile unsigned int _bdv_status = 0;
73in_unckdata volatile unsigned int _bdv_gtid;
74
75///////////////////////////////////////////////////////////////////////////////
76// This low_level function returns the value contained in register (index).
77///////////////////////////////////////////////////////////////////////////////
78unsigned int _bdv_get_register( unsigned int index )
79{
[320]80    unsigned int* vaddr = (unsigned int*)SEG_IOC_BASE + index;
[295]81    return _io_extended_read( vaddr );
82}
83
84///////////////////////////////////////////////////////////////////////////////
85// This low-level function set a new value in register (index).
86///////////////////////////////////////////////////////////////////////////////
87void _bdv_set_register( unsigned int index,
88                        unsigned int value ) 
89{
[320]90    unsigned int* vaddr = (unsigned int*)SEG_IOC_BASE + index;
[295]91    _io_extended_write( vaddr, value );
92}
93
94///////////////////////////////////////////////////////////////////////////////
[283]95// This function transfer data between a memory buffer and the block device.
96// The buffer lentgth is (count*block_size) bytes.
97// Arguments are:
98// - to_mem     : from external storage to memory when non 0.
[289]99// - mode       : BOOT / KERNEL / USER
[283]100// - lba        : first block index on the external storage.
[289]101// - buf_paddr  : physical base address of the memory buffer.
[283]102// - count      : number of blocks to be transfered.
103// Returns 0 if success, > 0 if error.
104///////////////////////////////////////////////////////////////////////////////
[295]105static unsigned int _bdv_access( unsigned int       to_mem,
106                                 unsigned int       mode,
107                                 unsigned int       lba,
108                                 unsigned long long buf_paddr,
109                                 unsigned int       count) 
[283]110{
111
[313]112#if GIET_DEBUG_IOC_DRIVER
[295]113unsigned int procid  = _get_procid();
114unsigned int cxy     = procid / NB_PROCS_MAX;
115unsigned int lpid    = procid % NB_PROCS_MAX;
116unsigned int x       = cxy >> Y_WIDTH;
117unsigned int y       = cxy & ((1<<Y_WIDTH) - 1);
118
119_printf("\n[BDV DEBUG] Processor[%d,%d,%d] enters _bdv_access() at cycle %d\n"
120        " - mode    = %d\n"
121        " - paddr   = %l\n"
122        " - sectors = %x\n"
123        " - lba     = %x\n",
124        x, y, lpid, _get_proctime(), mode, buf_paddr, count, lba );
[283]125#endif
126
[295]127    unsigned int       error = 0;
[283]128
[295]129    // get the lock protecting BDV
130    _get_lock(&_bdv_lock);
[283]131
[295]132    // set device registers
133    _bdv_set_register( BLOCK_DEVICE_BUFFER    , (unsigned int)buf_paddr );
134    _bdv_set_register( BLOCK_DEVICE_BUFFER_EXT, (unsigned int)(buf_paddr>>32) );
135    _bdv_set_register( BLOCK_DEVICE_COUNT     , count );
136    _bdv_set_register( BLOCK_DEVICE_LBA       , lba );
[283]137
[333]138#if GIET_DEBUG_IOC_DRIVER
139_printf("\n[BDV DEBUG] _bdv_access() : config registers set\n");
140#endif
141
[295]142    // In BOOT mode, we launch transfer, and poll the BDV_STATUS
143    // register because IRQs are masked.
144    if ( mode == IOC_BOOT_MODE ) 
[283]145    {
146        // Launch transfert
[295]147        if (to_mem == 0) _bdv_set_register( BLOCK_DEVICE_OP, BLOCK_DEVICE_WRITE );
148        else             _bdv_set_register( BLOCK_DEVICE_OP, BLOCK_DEVICE_READ );
[283]149
[333]150#if GIET_DEBUG_IOC_DRIVER
151_printf("\n[BDV DEBUG] _bdv_access() : transfert lauched in polling mode\n");
152#endif
[283]153        unsigned int status;
[289]154        do
[283]155        {
[295]156            status = _bdv_get_register( BLOCK_DEVICE_STATUS );
[283]157
[313]158#if GIET_DEBUG_IOC_DRIVER
[295]159_printf("\n[BDV DEBUG] _bdv_access() : ... waiting on BDV_STATUS register ...\n");
[283]160#endif
[289]161        }
162        while( (status != BLOCK_DEVICE_READ_SUCCESS)  &&
163               (status != BLOCK_DEVICE_READ_ERROR)    &&
164               (status != BLOCK_DEVICE_WRITE_SUCCESS) &&
[295]165               (status != BLOCK_DEVICE_WRITE_ERROR)   );      // busy waiting
[283]166
167        // analyse status
168        error = ( (status == BLOCK_DEVICE_READ_ERROR) ||
169                  (status == BLOCK_DEVICE_WRITE_ERROR) );
170
171        // release lock
[295]172        _release_lock(&_bdv_lock);     
[283]173    }
[295]174    // in USER or KERNEL mode, we deschedule the task.
175    // When the task is rescheduled, we check the _bdv_status variable,
176    // and release the lock.
177    // We need a critical section, because we must reset the RUN bit
178        // before to launch the transfer, and we don't want to be descheduled
179        // between these two operations.
180    else
[283]181    {
[295]182        unsigned int save_sr;
183        unsigned int ltid = _get_current_task_id();
184        unsigned int gpid = _get_procid();
[283]185
[295]186        // activates BDV interrupts
187        _bdv_set_register( BLOCK_DEVICE_IRQ_ENABLE, 1 );
188
189        // set the _bdv_status variable
190        _bdv_status = BLOCK_DEVICE_BUSY;
191
192        // enters critical section
193        _it_disable( &save_sr ); 
[320]194
[295]195        // set _bdv_gtid and reset runnable
196        _bdv_gtid = (gpid<<16) + ltid;
197        _set_task_slot( gpid, ltid, CTX_RUN_ID, 0 ); 
[283]198       
[295]199        // launch transfer
200        if (to_mem == 0) _bdv_set_register( BLOCK_DEVICE_OP, BLOCK_DEVICE_WRITE );
201        else             _bdv_set_register( BLOCK_DEVICE_OP, BLOCK_DEVICE_READ  );
[283]202
[333]203#if GIET_DEBUG_IOC_DRIVER
204_printf("\n[BDV DEBUG] _bdv_access() : transfert lauched in interrupt mode\n");
205#endif
[283]206        // deschedule task
207        _ctx_switch();                     
208
[295]209        // restore SR
210        _it_restore( &save_sr );
211
[283]212        // analyse status
[295]213        error = ( (_bdv_status == BLOCK_DEVICE_READ_ERROR) ||
214                  (_bdv_status == BLOCK_DEVICE_WRITE_ERROR) );
[283]215
[295]216        // reset _bdv_status and release lock
217        _bdv_status = BLOCK_DEVICE_IDLE; 
218        _release_lock(&_bdv_lock);     
[283]219    }
220
[313]221#if GIET_DEBUG_IOC_DRIVER
[295]222_printf("\n[BDV DEBUG] Processor[%d,%d,%d] exit _bdv_access() at cycle %d\n",
223        x, y, lpid, _get_proctime() );
[283]224#endif
225
226    return error;
227} // end _bdv_access()
228
229///////////////////////////////////////////////////////////////////////////////
[295]230// This function cheks block size, and desactivates the interrupts.
[283]231// Return 0 for success, > 0 if error
232///////////////////////////////////////////////////////////////////////////////
[295]233unsigned int _bdv_init()
[283]234{
[295]235    if ( _bdv_get_register( BLOCK_DEVICE_BLOCK_SIZE ) != 512 )
[283]236    {
[295]237        _printf("\n[GIET ERROR] in _bdv_init() : block size must be 512 bytes\n");
[283]238        return 1; 
239    }
240
[295]241    _bdv_set_register( BLOCK_DEVICE_IRQ_ENABLE, 0 );
[283]242    return 0;
243}
244
245///////////////////////////////////////////////////////////////////////////////
246// Transfer data from the block device to a memory buffer.
247// - mode     : BOOT / KERNEL / USER
248// - lba      : first block index on the block device
249// - buffer   : base address of the memory buffer (must be word aligned)
250// - count    : number of blocks to be transfered.
251// Returns 0 if success, > 0 if error.
252///////////////////////////////////////////////////////////////////////////////
[295]253unsigned int _bdv_read( unsigned int       mode, 
254                        unsigned int       lba, 
255                        unsigned long long buffer, 
256                        unsigned int       count) 
[283]257{
258    return _bdv_access( 1,        // read access
259                        mode, 
260                        lba,
261                        buffer,
262                        count );
263}
264
265///////////////////////////////////////////////////////////////////////////////
266// Transfer data from a memory buffer to the block device.
267// - mode     : BOOT / KERNEL / USER
268// - lba      : first block index on the block device
269// - buffer   : base address of the memory buffer (must be word aligned)
270// - count    : number of blocks to be transfered.
271// Returns 0 if success, > 0 if error.
272///////////////////////////////////////////////////////////////////////////////
[295]273unsigned int _bdv_write( unsigned int       mode, 
274                         unsigned int       lba, 
275                         unsigned long long buffer, 
276                         unsigned int       count ) 
[283]277{
278    return _bdv_access( 0,        // write access
279                        mode, 
280                        lba,
281                        buffer,
282                        count );
283}
284
285///////////////////////////////////////////////////////////////////////////////
[295]286// Returns device status.
[283]287///////////////////////////////////////////////////////////////////////////////
[295]288unsigned int _bdv_get_status()
[283]289{
[295]290    return _bdv_get_register( BLOCK_DEVICE_STATUS );
[283]291}
292
293///////////////////////////////////////////////////////////////////////////////
[295]294// Returns block size.
[283]295///////////////////////////////////////////////////////////////////////////////
[295]296unsigned int _bdv_get_block_size()
[283]297{
[295]298    return _bdv_get_register( BLOCK_DEVICE_BLOCK_SIZE );
[283]299}
300
[295]301///////////////////////////////////////////////////////////////////////////////////
302// This ISR save the status, acknowledge the IRQ,
303// and activates the task waiting on IO transfer.
304// It can be an HWI or a SWI.
305//
306// TODO the _set_task_slot access should be replaced by an atomic LL/SC
307//      when the CTX_RUN bool will be replaced by a bit_vector.
308///////////////////////////////////////////////////////////////////////////////////
309void _bdv_isr( unsigned int irq_type,   // HWI / WTI
310               unsigned int irq_id,     // index returned by ICU
311               unsigned int channel )   // unused
312{
[297]313    // get BDV status (and reset IRQ)
314    unsigned int status =  _bdv_get_register( BLOCK_DEVICE_STATUS ); 
[295]315
[297]316    // check status: does nothing if IDLE or BUSY
317    if ( (status == BLOCK_DEVICE_IDLE) ||
318         (status == BLOCK_DEVICE_BUSY) )   return;
319 
320    // save status in kernel buffer _bdv_status
321    _bdv_status = status; 
[295]322
323    // identify task waiting on BDV
[320]324    unsigned int rprocid     = _bdv_gtid>>16;
325    unsigned int ltid        = _bdv_gtid & 0xFFFF;
326    unsigned int remote_xy   = rprocid / NB_PROCS_MAX;
327    unsigned int remote_lpid = rprocid % NB_PROCS_MAX;
[295]328
[297]329    // re-activates sleeping task
330    _set_task_slot( rprocid,     // global processor index
331                    ltid,        // local task index on processor
332                    CTX_RUN_ID,  // CTX_RUN slot
333                    1 );         // running
334
335    // requires a context switch for remote processor running the waiting task
[320]336    _xcu_send_wti( remote_xy,    // remote cluster index
337                   remote_lpid,  // remote local processor index
[297]338                   0 );          // don't force context switch if not idle
339
[295]340#if GIET_DEBUG_IRQS  // we don't take the TTY lock to avoid deadlock
[320]341unsigned int procid     = _get_procid();
342unsigned int cluster_xy = procid / NB_PROCS_MAX;
343unsigned int lpid       = procid % NB_PROCS_MAX;
[295]344unsigned int x              = cluster_xy >> Y_WIDTH;
345unsigned int y              = cluster_xy & ((1<<Y_WIDTH)-1);
346unsigned int rx             = remote_xy >> Y_WIDTH;
347unsigned int ry             = remote_xy & ((1<<Y_WIDTH)-1);
348_puts("\n[IRQS DEBUG] Processor[");
349_putd(x );
350_puts(",");
351_putd(y );
352_puts(",");
353_putd(lpid );
354_puts("] enters _bdv_isr() at cycle ");
355_putd(_get_proctime() );
356_puts("\n  for task ");
357_putd(ltid );
358_puts(" running on processor[");
359_putd(rx );
360_puts(",");
361_putd(ry );
362_puts(",");
[320]363_putd(remote_lpid );
[295]364_puts(" / bdv status = ");
365_putx(_bdv_status );
366_puts("\n");
367#endif
368
369}
370
371
[283]372// Local Variables:
373// tab-width: 4
374// c-basic-offset: 4
375// c-file-offsets:((innamespace . 0)(inline-open . 0))
376// indent-tabs-mode: nil
377// End:
378// vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
379
Note: See TracBrowser for help on using the repository browser.