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
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// The _bdv_read() and _bdv_write() functions are always blocking.
13// They can be called in 3 modes:
14//
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).
19//
20// - In KERNEL mode, these functions use a descheduling strategy:
21//   The ISR executed when transfer completes should restart the calling task.
22//   There is no checking of user access right to the memory buffer.
23//   This mode must be used, for an "open" system call.
24//
25// - In USER mode, these functions use a descheduling strategy:
26//   The ISR executed when transfer completes should restart the calling task,
27//   The user access right to the memory buffer must be checked.
28//   This mode must be used for a "read/write" system call.
29//
30// As the BDV component can be used by several programs running in parallel,
31// the _bdv_lock variable guaranties exclusive access to the device.  The
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.
43//
44// The SEG_IOC_BASE address must be defined in the hard_config.h file.
45///////////////////////////////////////////////////////////////////////////////////
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.
54///////////////////////////////////////////////////////////////////////////////////
55
56#include <giet_config.h>
57#include <hard_config.h>
58#include <bdv_driver.h>
59#include <xcu_driver.h>
60#include <ioc_driver.h>
61#include <utils.h>
62#include <tty_driver.h>
63#include <ctx_handler.h>
64
65///////////////////////////////////////////////////////////////////////////////
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{
80    unsigned int* vaddr = (unsigned int*)SEG_IOC_BASE + index;
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{
90    unsigned int* vaddr = (unsigned int*)SEG_IOC_BASE + index;
91    _io_extended_write( vaddr, value );
92}
93
94///////////////////////////////////////////////////////////////////////////////
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.
99// - mode       : BOOT / KERNEL / USER
100// - lba        : first block index on the external storage.
101// - buf_paddr  : physical base address of the memory buffer.
102// - count      : number of blocks to be transfered.
103// Returns 0 if success, > 0 if error.
104///////////////////////////////////////////////////////////////////////////////
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) 
110{
111
112#if GIET_DEBUG_IOC_DRIVER
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 );
125#endif
126
127    unsigned int       error = 0;
128
129    // get the lock protecting BDV
130    _get_lock(&_bdv_lock);
131
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 );
137
138#if GIET_DEBUG_IOC_DRIVER
139_printf("\n[BDV DEBUG] _bdv_access() : config registers set\n");
140#endif
141
142    // In BOOT mode, we launch transfer, and poll the BDV_STATUS
143    // register because IRQs are masked.
144    if ( mode == IOC_BOOT_MODE ) 
145    {
146        // Launch transfert
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 );
149
150#if GIET_DEBUG_IOC_DRIVER
151_printf("\n[BDV DEBUG] _bdv_access() : transfert lauched in polling mode\n");
152#endif
153        unsigned int status;
154        do
155        {
156            status = _bdv_get_register( BLOCK_DEVICE_STATUS );
157
158#if GIET_DEBUG_IOC_DRIVER
159_printf("\n[BDV DEBUG] _bdv_access() : ... waiting on BDV_STATUS register ...\n");
160#endif
161        }
162        while( (status != BLOCK_DEVICE_READ_SUCCESS)  &&
163               (status != BLOCK_DEVICE_READ_ERROR)    &&
164               (status != BLOCK_DEVICE_WRITE_SUCCESS) &&
165               (status != BLOCK_DEVICE_WRITE_ERROR)   );      // busy waiting
166
167        // analyse status
168        error = ( (status == BLOCK_DEVICE_READ_ERROR) ||
169                  (status == BLOCK_DEVICE_WRITE_ERROR) );
170
171        // release lock
172        _release_lock(&_bdv_lock);     
173    }
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
181    {
182        unsigned int save_sr;
183        unsigned int ltid = _get_current_task_id();
184        unsigned int gpid = _get_procid();
185
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 ); 
194
195        // set _bdv_gtid and reset runnable
196        _bdv_gtid = (gpid<<16) + ltid;
197        _set_task_slot( gpid, ltid, CTX_RUN_ID, 0 ); 
198       
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  );
202
203#if GIET_DEBUG_IOC_DRIVER
204_printf("\n[BDV DEBUG] _bdv_access() : transfert lauched in interrupt mode\n");
205#endif
206        // deschedule task
207        _ctx_switch();                     
208
209        // restore SR
210        _it_restore( &save_sr );
211
212        // analyse status
213        error = ( (_bdv_status == BLOCK_DEVICE_READ_ERROR) ||
214                  (_bdv_status == BLOCK_DEVICE_WRITE_ERROR) );
215
216        // reset _bdv_status and release lock
217        _bdv_status = BLOCK_DEVICE_IDLE; 
218        _release_lock(&_bdv_lock);     
219    }
220
221#if GIET_DEBUG_IOC_DRIVER
222_printf("\n[BDV DEBUG] Processor[%d,%d,%d] exit _bdv_access() at cycle %d\n",
223        x, y, lpid, _get_proctime() );
224#endif
225
226    return error;
227} // end _bdv_access()
228
229///////////////////////////////////////////////////////////////////////////////
230// This function cheks block size, and desactivates the interrupts.
231// Return 0 for success, > 0 if error
232///////////////////////////////////////////////////////////////////////////////
233unsigned int _bdv_init()
234{
235    if ( _bdv_get_register( BLOCK_DEVICE_BLOCK_SIZE ) != 512 )
236    {
237        _printf("\n[GIET ERROR] in _bdv_init() : block size must be 512 bytes\n");
238        return 1; 
239    }
240
241    _bdv_set_register( BLOCK_DEVICE_IRQ_ENABLE, 0 );
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///////////////////////////////////////////////////////////////////////////////
253unsigned int _bdv_read( unsigned int       mode, 
254                        unsigned int       lba, 
255                        unsigned long long buffer, 
256                        unsigned int       count) 
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///////////////////////////////////////////////////////////////////////////////
273unsigned int _bdv_write( unsigned int       mode, 
274                         unsigned int       lba, 
275                         unsigned long long buffer, 
276                         unsigned int       count ) 
277{
278    return _bdv_access( 0,        // write access
279                        mode, 
280                        lba,
281                        buffer,
282                        count );
283}
284
285///////////////////////////////////////////////////////////////////////////////
286// Returns device status.
287///////////////////////////////////////////////////////////////////////////////
288unsigned int _bdv_get_status()
289{
290    return _bdv_get_register( BLOCK_DEVICE_STATUS );
291}
292
293///////////////////////////////////////////////////////////////////////////////
294// Returns block size.
295///////////////////////////////////////////////////////////////////////////////
296unsigned int _bdv_get_block_size()
297{
298    return _bdv_get_register( BLOCK_DEVICE_BLOCK_SIZE );
299}
300
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{
313    // get BDV status (and reset IRQ)
314    unsigned int status =  _bdv_get_register( BLOCK_DEVICE_STATUS ); 
315
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; 
322
323    // identify task waiting on BDV
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;
328
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
336    _xcu_send_wti( remote_xy,    // remote cluster index
337                   remote_lpid,  // remote local processor index
338                   0 );          // don't force context switch if not idle
339
340#if GIET_DEBUG_IRQS  // we don't take the TTY lock to avoid deadlock
341unsigned int procid     = _get_procid();
342unsigned int cluster_xy = procid / NB_PROCS_MAX;
343unsigned int lpid       = procid % NB_PROCS_MAX;
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(",");
363_putd(remote_lpid );
364_puts(" / bdv status = ");
365_putx(_bdv_status );
366_puts("\n");
367#endif
368
369}
370
371
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.