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

Last change on this file since 323 was 320, checked in by alain, 10 years ago

All drivers have been modified to use only the information
contained in the hard_config.h file

File size: 14.0 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    // In BOOT mode, we launch transfer, and poll the BDV_STATUS
139    // register because IRQs are masked.
140    if ( mode == IOC_BOOT_MODE ) 
141    {
142        // Launch transfert
143        if (to_mem == 0) _bdv_set_register( BLOCK_DEVICE_OP, BLOCK_DEVICE_WRITE );
144        else             _bdv_set_register( BLOCK_DEVICE_OP, BLOCK_DEVICE_READ );
145
146        unsigned int status;
147        do
148        {
149            status = _bdv_get_register( BLOCK_DEVICE_STATUS );
150
151#if GIET_DEBUG_IOC_DRIVER
152_printf("\n[BDV DEBUG] _bdv_access() : ... waiting on BDV_STATUS register ...\n");
153#endif
154        }
155        while( (status != BLOCK_DEVICE_READ_SUCCESS)  &&
156               (status != BLOCK_DEVICE_READ_ERROR)    &&
157               (status != BLOCK_DEVICE_WRITE_SUCCESS) &&
158               (status != BLOCK_DEVICE_WRITE_ERROR)   );      // busy waiting
159
160        // analyse status
161        error = ( (status == BLOCK_DEVICE_READ_ERROR) ||
162                  (status == BLOCK_DEVICE_WRITE_ERROR) );
163
164        // release lock
165        _release_lock(&_bdv_lock);     
166    }
167    // in USER or KERNEL mode, we deschedule the task.
168    // When the task is rescheduled, we check the _bdv_status variable,
169    // and release the lock.
170    // We need a critical section, because we must reset the RUN bit
171        // before to launch the transfer, and we don't want to be descheduled
172        // between these two operations.
173    else
174    {
175        unsigned int save_sr;
176        unsigned int ltid = _get_current_task_id();
177        unsigned int gpid = _get_procid();
178
179        // activates BDV interrupts
180        _bdv_set_register( BLOCK_DEVICE_IRQ_ENABLE, 1 );
181
182        // set the _bdv_status variable
183        _bdv_status = BLOCK_DEVICE_BUSY;
184
185        // enters critical section
186        _it_disable( &save_sr ); 
187       
188
189        // set _bdv_gtid and reset runnable
190        _bdv_gtid = (gpid<<16) + ltid;
191        _set_task_slot( gpid, ltid, CTX_RUN_ID, 0 ); 
192       
193        // launch transfer
194        if (to_mem == 0) _bdv_set_register( BLOCK_DEVICE_OP, BLOCK_DEVICE_WRITE );
195        else             _bdv_set_register( BLOCK_DEVICE_OP, BLOCK_DEVICE_READ  );
196
197        // deschedule task
198        _ctx_switch();                     
199
200        // restore SR
201        _it_restore( &save_sr );
202
203        // analyse status
204        error = ( (_bdv_status == BLOCK_DEVICE_READ_ERROR) ||
205                  (_bdv_status == BLOCK_DEVICE_WRITE_ERROR) );
206
207        // reset _bdv_status and release lock
208        _bdv_status = BLOCK_DEVICE_IDLE; 
209        _release_lock(&_bdv_lock);     
210    }
211
212#if GIET_DEBUG_IOC_DRIVER
213_printf("\n[BDV DEBUG] Processor[%d,%d,%d] exit _bdv_access() at cycle %d\n",
214        x, y, lpid, _get_proctime() );
215#endif
216
217    return error;
218} // end _bdv_access()
219
220///////////////////////////////////////////////////////////////////////////////
221// This function cheks block size, and desactivates the interrupts.
222// Return 0 for success, > 0 if error
223///////////////////////////////////////////////////////////////////////////////
224unsigned int _bdv_init()
225{
226    if ( _bdv_get_register( BLOCK_DEVICE_BLOCK_SIZE ) != 512 )
227    {
228        _printf("\n[GIET ERROR] in _bdv_init() : block size must be 512 bytes\n");
229        return 1; 
230    }
231
232    _bdv_set_register( BLOCK_DEVICE_IRQ_ENABLE, 0 );
233    return 0;
234}
235
236///////////////////////////////////////////////////////////////////////////////
237// Transfer data from the block device to a memory buffer.
238// - mode     : BOOT / KERNEL / USER
239// - lba      : first block index on the block device
240// - buffer   : base address of the memory buffer (must be word aligned)
241// - count    : number of blocks to be transfered.
242// Returns 0 if success, > 0 if error.
243///////////////////////////////////////////////////////////////////////////////
244unsigned int _bdv_read( unsigned int       mode, 
245                        unsigned int       lba, 
246                        unsigned long long buffer, 
247                        unsigned int       count) 
248{
249    return _bdv_access( 1,        // read access
250                        mode, 
251                        lba,
252                        buffer,
253                        count );
254}
255
256///////////////////////////////////////////////////////////////////////////////
257// Transfer data from a memory buffer to the block device.
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_write( unsigned int       mode, 
265                         unsigned int       lba, 
266                         unsigned long long buffer, 
267                         unsigned int       count ) 
268{
269    return _bdv_access( 0,        // write access
270                        mode, 
271                        lba,
272                        buffer,
273                        count );
274}
275
276///////////////////////////////////////////////////////////////////////////////
277// Returns device status.
278///////////////////////////////////////////////////////////////////////////////
279unsigned int _bdv_get_status()
280{
281    return _bdv_get_register( BLOCK_DEVICE_STATUS );
282}
283
284///////////////////////////////////////////////////////////////////////////////
285// Returns block size.
286///////////////////////////////////////////////////////////////////////////////
287unsigned int _bdv_get_block_size()
288{
289    return _bdv_get_register( BLOCK_DEVICE_BLOCK_SIZE );
290}
291
292///////////////////////////////////////////////////////////////////////////////////
293// This ISR save the status, acknowledge the IRQ,
294// and activates the task waiting on IO transfer.
295// It can be an HWI or a SWI.
296//
297// TODO the _set_task_slot access should be replaced by an atomic LL/SC
298//      when the CTX_RUN bool will be replaced by a bit_vector.
299///////////////////////////////////////////////////////////////////////////////////
300void _bdv_isr( unsigned int irq_type,   // HWI / WTI
301               unsigned int irq_id,     // index returned by ICU
302               unsigned int channel )   // unused
303{
304    // get BDV status (and reset IRQ)
305    unsigned int status =  _bdv_get_register( BLOCK_DEVICE_STATUS ); 
306
307    // check status: does nothing if IDLE or BUSY
308    if ( (status == BLOCK_DEVICE_IDLE) ||
309         (status == BLOCK_DEVICE_BUSY) )   return;
310 
311    // save status in kernel buffer _bdv_status
312    _bdv_status = status; 
313
314    // identify task waiting on BDV
315    unsigned int rprocid     = _bdv_gtid>>16;
316    unsigned int ltid        = _bdv_gtid & 0xFFFF;
317    unsigned int remote_xy   = rprocid / NB_PROCS_MAX;
318    unsigned int remote_lpid = rprocid % NB_PROCS_MAX;
319
320    // re-activates sleeping task
321    _set_task_slot( rprocid,     // global processor index
322                    ltid,        // local task index on processor
323                    CTX_RUN_ID,  // CTX_RUN slot
324                    1 );         // running
325
326    // requires a context switch for remote processor running the waiting task
327    _xcu_send_wti( remote_xy,    // remote cluster index
328                   remote_lpid,  // remote local processor index
329                   0 );          // don't force context switch if not idle
330
331#if GIET_DEBUG_IRQS  // we don't take the TTY lock to avoid deadlock
332unsigned int procid     = _get_procid();
333unsigned int cluster_xy = procid / NB_PROCS_MAX;
334unsigned int lpid       = procid % NB_PROCS_MAX;
335unsigned int x              = cluster_xy >> Y_WIDTH;
336unsigned int y              = cluster_xy & ((1<<Y_WIDTH)-1);
337unsigned int rx             = remote_xy >> Y_WIDTH;
338unsigned int ry             = remote_xy & ((1<<Y_WIDTH)-1);
339_puts("\n[IRQS DEBUG] Processor[");
340_putd(x );
341_puts(",");
342_putd(y );
343_puts(",");
344_putd(lpid );
345_puts("] enters _bdv_isr() at cycle ");
346_putd(_get_proctime() );
347_puts("\n  for task ");
348_putd(ltid );
349_puts(" running on processor[");
350_putd(rx );
351_puts(",");
352_putd(ry );
353_puts(",");
354_putd(remote_lpid );
355_puts(" / bdv status = ");
356_putx(_bdv_status );
357_puts("\n");
358#endif
359
360}
361
362
363// Local Variables:
364// tab-width: 4
365// c-basic-offset: 4
366// c-file-offsets:((innamespace . 0)(inline-open . 0))
367// indent-tabs-mode: nil
368// End:
369// vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
370
Note: See TracBrowser for help on using the repository browser.