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

Last change on this file since 355 was 350, checked in by alain, 10 years ago

Introducing two modifications regarding the locks protecting
exclusive access to BDV and TTY peripherals channels:

  • use the giet_lock_t type to have only one lock per cache line.
  • store these locks in the seg_kernel_data or in seg_kernel_uncdata, depending on the GIET_NO HARD_CC configuration parameter, to have cacheable locks when it is possible.
File size: 14.7 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#if !defined(GIET_NO_HARD_CC)
66# error: You must define GIET_NO_HARD_CC in the giet_config.h file
67#endif
68
69///////////////////////////////////////////////////////////////////////////////
70// BDV global variables
71///////////////////////////////////////////////////////////////////////////////
72
73#define in_unckdata __attribute__((section (".unckdata")))
74#define in_kdata __attribute__((section (".kdata")))
75
76#if GIET_NO_HARD_CC
77in_unckdata giet_lock_t           _bdv_lock __attribute__((aligned(64)));
78in_unckdata volatile unsigned int _bdv_status;
79in_unckdata volatile unsigned int _bdv_gtid;
80#else
81in_kdata giet_lock_t           _bdv_lock __attribute__((aligned(64)));
82in_kdata volatile unsigned int _bdv_status;
83in_kdata volatile unsigned int _bdv_gtid;
84#endif
85
86///////////////////////////////////////////////////////////////////////////////
87// This low_level function returns the value contained in register (index).
88///////////////////////////////////////////////////////////////////////////////
89unsigned int _bdv_get_register( unsigned int index )
90{
91    unsigned int* vaddr = (unsigned int*)SEG_IOC_BASE + index;
92    return _io_extended_read( vaddr );
93}
94
95///////////////////////////////////////////////////////////////////////////////
96// This low-level function set a new value in register (index).
97///////////////////////////////////////////////////////////////////////////////
98void _bdv_set_register( unsigned int index,
99                        unsigned int value ) 
100{
101    unsigned int* vaddr = (unsigned int*)SEG_IOC_BASE + index;
102    _io_extended_write( vaddr, value );
103}
104
105///////////////////////////////////////////////////////////////////////////////
106// This function transfer data between a memory buffer and the block device.
107// The buffer lentgth is (count*block_size) bytes.
108// Arguments are:
109// - to_mem     : from external storage to memory when non 0.
110// - mode       : BOOT / KERNEL / USER
111// - lba        : first block index on the external storage.
112// - buf_paddr  : physical base address of the memory buffer.
113// - count      : number of blocks to be transfered.
114// Returns 0 if success, > 0 if error.
115///////////////////////////////////////////////////////////////////////////////
116static unsigned int _bdv_access( unsigned int       to_mem,
117                                 unsigned int       mode,
118                                 unsigned int       lba,
119                                 unsigned long long buf_paddr,
120                                 unsigned int       count) 
121{
122
123#if GIET_DEBUG_IOC_DRIVER
124unsigned int procid  = _get_procid();
125unsigned int cxy     = procid / NB_PROCS_MAX;
126unsigned int lpid    = procid % NB_PROCS_MAX;
127unsigned int x       = cxy >> Y_WIDTH;
128unsigned int y       = cxy & ((1<<Y_WIDTH) - 1);
129
130_printf("\n[BDV DEBUG] Processor[%d,%d,%d] enters _bdv_access() at cycle %d\n"
131        " - mode    = %d\n"
132        " - paddr   = %l\n"
133        " - sectors = %x\n"
134        " - lba     = %x\n",
135        x, y, lpid, _get_proctime(), mode, buf_paddr, count, lba );
136#endif
137
138    unsigned int       error = 0;
139
140    // get the lock protecting BDV
141    _get_lock(&_bdv_lock);
142
143    // set device registers
144    _bdv_set_register( BLOCK_DEVICE_BUFFER    , (unsigned int)buf_paddr );
145    _bdv_set_register( BLOCK_DEVICE_BUFFER_EXT, (unsigned int)(buf_paddr>>32) );
146    _bdv_set_register( BLOCK_DEVICE_COUNT     , count );
147    _bdv_set_register( BLOCK_DEVICE_LBA       , lba );
148
149#if GIET_DEBUG_IOC_DRIVER
150_printf("\n[BDV DEBUG] _bdv_access() : config registers set\n");
151#endif
152
153    // In BOOT mode, we launch transfer, and poll the BDV_STATUS
154    // register because IRQs are masked.
155    if ( mode == IOC_BOOT_MODE ) 
156    {
157        // Launch transfert
158        if (to_mem == 0) _bdv_set_register( BLOCK_DEVICE_OP, BLOCK_DEVICE_WRITE );
159        else             _bdv_set_register( BLOCK_DEVICE_OP, BLOCK_DEVICE_READ );
160
161#if GIET_DEBUG_IOC_DRIVER
162_printf("\n[BDV DEBUG] _bdv_access() : transfert lauched in polling mode\n");
163#endif
164        unsigned int status;
165        do
166        {
167            status = _bdv_get_register( BLOCK_DEVICE_STATUS );
168
169#if GIET_DEBUG_IOC_DRIVER
170_printf("\n[BDV DEBUG] _bdv_access() : ... waiting on BDV_STATUS register ...\n");
171#endif
172        }
173        while( (status != BLOCK_DEVICE_READ_SUCCESS)  &&
174               (status != BLOCK_DEVICE_READ_ERROR)    &&
175               (status != BLOCK_DEVICE_WRITE_SUCCESS) &&
176               (status != BLOCK_DEVICE_WRITE_ERROR)   );      // busy waiting
177
178        // analyse status
179        error = ( (status == BLOCK_DEVICE_READ_ERROR) ||
180                  (status == BLOCK_DEVICE_WRITE_ERROR) );
181
182        // release lock
183        _release_lock(&_bdv_lock);     
184    }
185    // in USER or KERNEL mode, we deschedule the task.
186    // When the task is rescheduled, we check the _bdv_status variable,
187    // and release the lock.
188    // We need a critical section, because we must reset the RUN bit
189        // before to launch the transfer, and we don't want to be descheduled
190        // between these two operations.
191    else
192    {
193        unsigned int save_sr;
194        unsigned int ltid = _get_current_task_id();
195        unsigned int gpid = _get_procid();
196
197        // activates BDV interrupts
198        _bdv_set_register( BLOCK_DEVICE_IRQ_ENABLE, 1 );
199
200        // set the _bdv_status variable
201        _bdv_status = BLOCK_DEVICE_BUSY;
202
203        // enters critical section
204        _it_disable( &save_sr ); 
205
206        // set _bdv_gtid and reset runnable
207        _bdv_gtid = (gpid<<16) + ltid;
208        _set_task_slot( gpid, ltid, CTX_RUN_ID, 0 ); 
209       
210        // launch transfer
211        if (to_mem == 0) _bdv_set_register( BLOCK_DEVICE_OP, BLOCK_DEVICE_WRITE );
212        else             _bdv_set_register( BLOCK_DEVICE_OP, BLOCK_DEVICE_READ  );
213
214#if GIET_DEBUG_IOC_DRIVER
215_printf("\n[BDV DEBUG] _bdv_access() : transfert lauched in interrupt mode\n");
216#endif
217        // deschedule task
218        _ctx_switch();                     
219
220        // restore SR
221        _it_restore( &save_sr );
222
223        // analyse status
224        error = ( (_bdv_status == BLOCK_DEVICE_READ_ERROR) ||
225                  (_bdv_status == BLOCK_DEVICE_WRITE_ERROR) );
226
227        // reset _bdv_status and release lock
228        _bdv_status = BLOCK_DEVICE_IDLE; 
229        _release_lock(&_bdv_lock);     
230    }
231
232#if GIET_DEBUG_IOC_DRIVER
233_printf("\n[BDV DEBUG] Processor[%d,%d,%d] exit _bdv_access() at cycle %d\n",
234        x, y, lpid, _get_proctime() );
235#endif
236
237    return error;
238} // end _bdv_access()
239
240///////////////////////////////////////////////////////////////////////////////
241// This function cheks block size, and desactivates the interrupts.
242// Return 0 for success, > 0 if error
243///////////////////////////////////////////////////////////////////////////////
244unsigned int _bdv_init()
245{
246    if ( _bdv_get_register( BLOCK_DEVICE_BLOCK_SIZE ) != 512 )
247    {
248        _printf("\n[GIET ERROR] in _bdv_init() : block size must be 512 bytes\n");
249        return 1; 
250    }
251
252    _bdv_set_register( BLOCK_DEVICE_IRQ_ENABLE, 0 );
253    return 0;
254}
255
256///////////////////////////////////////////////////////////////////////////////
257// Transfer data from the block device to a memory buffer.
258// - mode     : BOOT / KERNEL / USER
259// - lba      : first block index on the block device
260// - buffer   : base address of the memory buffer (must be word aligned)
261// - count    : number of blocks to be transfered.
262// Returns 0 if success, > 0 if error.
263///////////////////////////////////////////////////////////////////////////////
264unsigned int _bdv_read( unsigned int       mode, 
265                        unsigned int       lba, 
266                        unsigned long long buffer, 
267                        unsigned int       count) 
268{
269    return _bdv_access( 1,        // read access
270                        mode, 
271                        lba,
272                        buffer,
273                        count );
274}
275
276///////////////////////////////////////////////////////////////////////////////
277// Transfer data from a memory buffer to the block device.
278// - mode     : BOOT / KERNEL / USER
279// - lba      : first block index on the block device
280// - buffer   : base address of the memory buffer (must be word aligned)
281// - count    : number of blocks to be transfered.
282// Returns 0 if success, > 0 if error.
283///////////////////////////////////////////////////////////////////////////////
284unsigned int _bdv_write( unsigned int       mode, 
285                         unsigned int       lba, 
286                         unsigned long long buffer, 
287                         unsigned int       count ) 
288{
289    return _bdv_access( 0,        // write access
290                        mode, 
291                        lba,
292                        buffer,
293                        count );
294}
295
296///////////////////////////////////////////////////////////////////////////////
297// Returns device status.
298///////////////////////////////////////////////////////////////////////////////
299unsigned int _bdv_get_status()
300{
301    return _bdv_get_register( BLOCK_DEVICE_STATUS );
302}
303
304///////////////////////////////////////////////////////////////////////////////
305// Returns block size.
306///////////////////////////////////////////////////////////////////////////////
307unsigned int _bdv_get_block_size()
308{
309    return _bdv_get_register( BLOCK_DEVICE_BLOCK_SIZE );
310}
311
312///////////////////////////////////////////////////////////////////////////////////
313// This ISR save the status, acknowledge the IRQ,
314// and activates the task waiting on IO transfer.
315// It can be an HWI or a SWI.
316//
317// TODO the _set_task_slot access should be replaced by an atomic LL/SC
318//      when the CTX_RUN bool will be replaced by a bit_vector.
319///////////////////////////////////////////////////////////////////////////////////
320void _bdv_isr( unsigned int irq_type,   // HWI / WTI
321               unsigned int irq_id,     // index returned by ICU
322               unsigned int channel )   // unused
323{
324    // get BDV status (and reset IRQ)
325    unsigned int status =  _bdv_get_register( BLOCK_DEVICE_STATUS ); 
326
327    // check status: does nothing if IDLE or BUSY
328    if ( (status == BLOCK_DEVICE_IDLE) ||
329         (status == BLOCK_DEVICE_BUSY) )   return;
330 
331    // save status in kernel buffer _bdv_status
332    _bdv_status = status; 
333
334    // identify task waiting on BDV
335    unsigned int rprocid     = _bdv_gtid>>16;
336    unsigned int ltid        = _bdv_gtid & 0xFFFF;
337    unsigned int remote_xy   = rprocid / NB_PROCS_MAX;
338    unsigned int remote_lpid = rprocid % NB_PROCS_MAX;
339
340    // re-activates sleeping task
341    _set_task_slot( rprocid,     // global processor index
342                    ltid,        // local task index on processor
343                    CTX_RUN_ID,  // CTX_RUN slot
344                    1 );         // running
345
346    // requires a context switch for remote processor running the waiting task
347    _xcu_send_wti( remote_xy,    // remote cluster index
348                   remote_lpid,  // remote local processor index
349                   0 );          // don't force context switch if not idle
350
351#if GIET_DEBUG_IRQS  // we don't take the TTY lock to avoid deadlock
352unsigned int procid     = _get_procid();
353unsigned int cluster_xy = procid / NB_PROCS_MAX;
354unsigned int lpid       = procid % NB_PROCS_MAX;
355unsigned int x              = cluster_xy >> Y_WIDTH;
356unsigned int y              = cluster_xy & ((1<<Y_WIDTH)-1);
357unsigned int rx             = remote_xy >> Y_WIDTH;
358unsigned int ry             = remote_xy & ((1<<Y_WIDTH)-1);
359_puts("\n[IRQS DEBUG] Processor[");
360_putd(x );
361_puts(",");
362_putd(y );
363_puts(",");
364_putd(lpid );
365_puts("] enters _bdv_isr() at cycle ");
366_putd(_get_proctime() );
367_puts("\n  for task ");
368_putd(ltid );
369_puts(" running on processor[");
370_putd(rx );
371_puts(",");
372_putd(ry );
373_puts(",");
374_putd(remote_lpid );
375_puts(" / bdv status = ");
376_putx(_bdv_status );
377_puts("\n");
378#endif
379
380}
381
382
383// Local Variables:
384// tab-width: 4
385// c-basic-offset: 4
386// c-file-offsets:((innamespace . 0)(inline-open . 0))
387// indent-tabs-mode: nil
388// End:
389// vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
390
Note: See TracBrowser for help on using the repository browser.