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

Last change on this file since 296 was 295, checked in by alain, 10 years ago

Introducing a major release, to suppoort the tsar_generic_leti platform
and the various (external or internal) peripherals configurations.
The map.xml format has been modified, in order to support the new
vci_iopic componentand a new policy for peripherals initialisation.
The IRQs are nom described in the XICU and IOPIC components
(and not anymore in the processors).
To enforce this major change, the map.xml file signature changed:
The signature value must be: 0xDACE2014

This new release has been tested on the tsar_generic_leti platform
for the following mappings:

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