source: soft/giet_vm/giet_drivers/hba_driver.c @ 289

Last change on this file since 289 was 289, checked in by cfuguet, 10 years ago

Modifications on GIET-VM IOC driver:

  • Introducing new layer on the IOC driver. Every call to ioc_read, ioc_write, ioc_get_block_size or ioc_init

functions will call the specific driver of the used IOC
controller. Supported IOC controllers are (for now) :

  1. BDV (Soclib Block Device)
  2. HBA
  3. SPI (SDCARD - SPI controller)
  • All functions of IOC controllers drivers respect the same interface.
  • To specify the used IOC controller of the platform, a subtype field has been introduced on the map.xml file. This subtype field must be declared on the IOC periph instantiation. Available subtypes (for now) : BDV, HBA or SPI.
File size: 18.8 KB
Line 
1///////////////////////////////////////////////////////////////////////////////////
2// File     : hba_driver.c
3// Date     : 23/11/2013
4// Author   : alain greiner and zhang
5// Copyright (c) UPMC-LIP6
6///////////////////////////////////////////////////////////////////////////////////
7// The hba_driver.c and hba_driver.h files are part ot the GIET-VM kernel.
8// This driver supports the SocLib VciMultiAhci component, that is a multi-channels,
9// block oriented, external storage contrÃŽler, respecting the AHCI standard.
10//
11// It can exist only one ahci-device controler in the architecture.
12//
13// The _ioc_read() and _ioc_write() functions use the _ioc_access() function,
14// that is always blocking, but can be called in 4 modes:
15//
16// - In BOOT_PA mode, the _ioc_access() function use the buffer virtual address
17//   as a physical address (as the page tables are not build) and use a polling
18//   policy on the IOC_STATUS register to detect transfer completion, as
19//   hardware interrupts are not activated. This mode is used by the
20//   boot code to load the map.bin file into memory.
21//
22// - In BOOT_VA mode, the _ioc_access() function makes a V2P translation to
23//   compute the buffer physical address, and use a polling policy on IOC_STATUS
24//   register to detect transfer completion. This mode is used by the boot code
25//   to load the various .elf files into memory.
26//
27// - In KERNEL mode, the _ioc_access() function makes a V2P translation to
28//   compute the buffer physical address, and use a descheduling strategy:
29//   The ISR executed when transfer completes should restart the calling task.
30//   There is no checking of user access right to the memory buffer.
31//   This mode must be used to access IOC, for an "open" system call.
32//
33// - In USER mode, the _ioc_access() function makes a V2P translation to
34//   compute the buffer physical address, and use a descheduling strategy:
35//   The ISR executed when transfer completes should restart the calling task,
36//   The user access right to the memory buffer must be checked.
37//   This mode must be used to access IOC, for a "read/write" system call.
38//
39// As the IOC component can be used by several programs running in parallel,
40// the _ioc_lock variable guaranties exclusive access to the device.  The
41// _ioc_read() and _ioc_write() functions use atomic LL/SC to get the lock.
42//
43// The IOMMU can be activated or not:
44//
45// 1) When the IOMMU is used, a fixed size 2Mbytes vseg is allocated to
46// the IOC peripheral, in the I/O virtual space, and the user buffer is
47// dynamically remapped in the IOMMU page table. The corresponding entry
48// in the IOMMU PT1 is defined by the kernel _ioc_iommu_ix1 variable.
49// The number of pages to be unmapped is stored in the _ioc_npages variable.
50// The number of PT2 entries is dynamically computed and stored in the
51// kernel _ioc_iommu_npages variable. It cannot be larger than 512.
52// The user buffer is unmapped by the _ioc_completed() function when
53// the transfer is completed.
54//
55// 2/ If the IOMMU is not used, we check that  the user buffer is mapped to a
56// contiguous physical buffer (this is generally true because the user space
57// page tables are statically constructed to use contiguous physical memory).
58//
59// Finally, the memory buffer must fulfill the following conditions:
60// - The buffer must be word aligned,
61// - The buffer must be mapped in user space for an user access,
62// - The buffer must be writable in case of (to_mem) access,
63// - The total number of physical pages occupied by the user buffer cannot
64//   be larger than 512 pages if the IOMMU is activated,
65// - All physical pages occupied by the user buffer must be contiguous
66//   if the IOMMU is not activated.
67// An error code is returned if these conditions are not verified.
68///////////////////////////////////////////////////////////////////////////////////
69// The seg_ioc_base virtual base addresses must be defined in giet_vsegs.ld file.
70///////////////////////////////////////////////////////////////////////////////////
71
72#include <giet_config.h>
73#include <ioc_driver.h>
74#include <utils.h>
75#include <tty_driver.h>
76#include <iob_driver.h>
77#include <ctx_handler.h>
78#include <mmc_driver.h>
79#include <hba_driver.h>
80#include <vmem.h>
81
82#if !defined( NB_HBA_CHANNELS )
83# error: You must define NB_HBA_CHANNELS in the hard_config.h file
84#endif
85
86#if ( NB_HBA_CHANNELS > 8 )
87# error: NB_HBA_CHANNELS cannot be larger than 8
88#endif
89
90#if !defined( USE_IOB )
91# error: You must define USE_IOB in the hard_config.h file
92#endif
93
94#if !defined(GIET_USE_IOMMU)
95# error: You must define GIET_USE_IOMMU in the giet_config.h file
96#endif
97
98#define in_unckdata __attribute__((section (".unckdata")))
99
100//////////////////////////////////////////////////////////////////
101//  Global variables
102//////////////////////////////////////////////////////////////////
103
104// command list array (one per channel)
105hba_cmd_list_t   hba_cmd_list[NB_HBA_CHANNELS] __attribute__((aligned(0x1000)));   
106
107// command tables array (32 command tables per channel)
108hba_cmd_table_t  hba_cmd_table[NB_HBA_CHANNELS][32] __attribute__((aligned(0x1000))); 
109
110// command list physical addresses array (one per channel)
111paddr_t          hba_cmd_list_paddr[NB_HBA_CHANNELS];
112
113// command tables physical addresses array (32 command tables per channel)
114paddr_t          hba_cmd_table_paddr[NB_HBA_CHANNELS][32];
115
116// command list pointer array (one per channel)
117unsigned int     hba_cmd_slot[NB_HBA_CHANNELS];
118
119//////////////////////////////////////////////////////////////////
120// This function returns the status of a given channel.
121// return 0 if success, >0 if error
122//////////////////////////////////////////////////////////////////
123unsigned int _hba_get_status( unsigned int   channel, 
124                              unsigned int*  status )
125{
126    volatile unsigned int* hba_address;
127    hba_address = (unsigned int*)(&seg_ioc_base) + (HBA_SPAN*channel);
128
129    if( channel >= NB_HBA_CHANNELS )
130    {
131        _tty_get_lock( 0 );
132        _puts("\n[GIET ERROR] in _hba_get_status() : illegal channel\n");
133        _tty_release_lock( 0 );
134        return 1;
135    }
136    else
137    {
138        *status = hba_address[HBA_PXIS];
139        return 0;
140    }
141}
142//////////////////////////////////////////////////////////////////
143// This function reset the status resgister for a given channel.
144// return 0 if success, >0 if error
145//////////////////////////////////////////////////////////////////
146unsigned int _hba_reset_status( unsigned int channel )
147{
148    volatile unsigned int* hba_address;
149    hba_address = (unsigned int*)(&seg_ioc_base) + (HBA_SPAN*channel);
150
151    if( channel >= NB_HBA_CHANNELS )
152    {   
153        _tty_get_lock( 0 );
154        _puts("\n[GIET ERROR] in _hba_reset_status() : illegal channel\n");
155        _tty_release_lock( 0 );
156        return 1;
157    }
158    else
159    {
160        hba_address[HBA_PXIS] = 0;
161        return 0;
162    }
163}
164///////////////////////////////////////////////////////////////////////////////
165// This function register a command in both the command list
166// and the command table, and updates the HBA_PXCI register.
167// It uses the AHCI Scatter/Gather mechanisme to split the user
168// buffer in several physical buffers, with the constraint that each physical
169// buffer must be an integer number of blocks entirely contained in a single
170// page frame.
171// return 0 if success, > 0 if error
172///////////////////////////////////////////////////////////////////////////////
173unsigned int _hba_cmd_set( unsigned int  is_read,     // to memory
174                           unsigned int  lba,         // logic block address
175                           unsigned int  buf_vaddr,   // buffer virtual address
176                           unsigned int  count )      // number of blocks
177{
178    volatile unsigned int *hba_address;
179
180    unsigned int       block_size;     // defined by the block device (bytes)
181    unsigned int       channel_id;     // channel index
182    unsigned int       pxci;           // command list status
183    unsigned int       cmd_id;         // command index in command list
184    unsigned int       buf_id;         // for physical buffers covering user buffer
185    unsigned int       user_pt_vbase;  // user page table virtual base address
186    unsigned int       vpn;            // for all pages covering the userbuffer
187    unsigned int       vpn_min;        // first virtual page index for user buffer
188    unsigned int       vpn_max;        // last  virtual page index for user buffer
189    unsigned int       offset;         // unaligned bytes in page frame: buf_vaddr & 0xFFF
190    unsigned int       offset_last;    // unaligned bytes in last frame
191    hba_cmd_desc_t*    cmd_desc;       // command descriptor pointer   
192    hba_cmd_table_t*   cmd_table;      // command table pointer
193
194    block_size = _hba_get_block_size();
195
196    // check buffer alignment
197    if( buf_vaddr & (block_size-1) )
198    {
199        _tty_get_lock( 0 );
200        _puts("\n[GIET ERROR] in _hba_set_cmd() : user buffer not block aligned\n");
201        _tty_release_lock( 0 );
202        return 1;
203    }
204
205    // get channel index
206    channel_id = _get_context_slot(CTX_HBA_ID);
207    if ( channel_id == 0xFFFFFFFF )
208    {
209        _tty_get_lock( 0 );
210        _puts("\n[GIET ERROR] in _hba_set_cmd() : no HBA channel allocated\n");
211        _tty_release_lock( 0 );
212        return 1;
213    }
214
215    // get hba device address
216    hba_address = (unsigned int*)(&seg_ioc_base) + (HBA_SPAN * channel_id);
217
218    // get command list status
219    pxci = hba_address[HBA_PXCI];
220
221    // get command index and return error if command list full
222    cmd_id = hba_cmd_slot[channel_id];
223    if( pxci & (1<<cmd_id ) ) 
224    {
225        _tty_get_lock( 0 );
226        _puts("\n[GIET ERROR] in _hba_set_cmd() : command list full in channel \n");
227        _putd( channel_id );
228        _puts("\n");
229        _tty_release_lock( 0 );
230        return 1;
231    }
232
233    // compute pointers on command descriptor and command table   
234    cmd_desc  = (hba_cmd_desc_t*)(&(hba_cmd_list[channel_id].desc[cmd_id]));
235    cmd_table = (hba_cmd_table_t*)(&(hba_cmd_table[channel_id][cmd_id]));
236
237    // get user space page table virtual address
238    user_pt_vbase     = _get_context_slot(CTX_PTAB_ID);
239    vpn_min           = buf_vaddr >> 12;
240    vpn_max           = (buf_vaddr + (block_size*count) - 1) >> 12;
241    offset            = buf_vaddr & 0xFFF;
242    offset_last       = (buf_vaddr + (block_size*count) - 1) & 0xFFF;
243
244    // initialize all buffer descriptors in command table
245    // (loop on all virtual pages covering the user buffer)
246    for( vpn = vpn_min, buf_id = 0 ; vpn <= vpn_max ; vpn++ )
247    {
248        paddr_t      paddr;
249        unsigned int count;
250        unsigned int ppn;
251        unsigned int flags;
252        unsigned int ko;
253        unsigned int buf_id = 0;
254
255        // get ppn and flags
256        ko = _v2p_translate( (page_table_t*)user_pt_vbase,
257                              vpn,
258                              &ppn,
259                              &flags );
260
261        // check access rights
262        if ( ko )
263        {
264            _tty_get_lock( 0 );
265            _puts("[GIET ERROR] in _hba_set_cmd() : user buffer unmapped\n");
266            _tty_release_lock( 0 );
267            return 1;
268        }
269        if ((flags & PTE_U) == 0)
270        {
271            _tty_get_lock( 0 );
272            _puts("[GIET ERROR] in _hba_set_cmd() : user buffer not in user space\n");
273            _tty_release_lock( 0 );
274            return 1;
275        }
276        if (((flags & PTE_W) == 0 ) && (is_read == 0) )
277        {
278            _tty_get_lock( 0 );
279            _puts("[GIET ERROR] in _hba_set_cmd() : user buffer not writable\n");
280            _tty_release_lock( 0 );
281            return 1;
282        }
283
284        // check buffer index overflow
285        if( buf_id > 245 )
286        {
287            _tty_get_lock( 0 );
288            _puts("[GIET ERROR] in _hba_set_cmd() : max number of buffers is 248\n");
289            _tty_release_lock( 0 );
290            return 1;
291        }
292
293        // buffer allocation
294        if( vpn == vpn_min )       // first page: one single buffer
295        {
296            paddr = (((paddr_t)ppn) << 12) + offset;
297            count = 0x1000 - offset;
298            cmd_table->entry[buf_id].dba  = (unsigned int)(paddr);
299            cmd_table->entry[buf_id].dbau = (unsigned int)(paddr >> 32);
300            cmd_table->entry[buf_id].dbc  = count;
301
302#if GIET_DEBUG_HBA_DRIVER
303_puts("\n- buf_index = ");
304_putd( buf_id );
305_puts(" / paddr = ");
306_putl( paddr );
307_puts(" / count = ");
308_putd( count );
309_puts("\n");
310#endif
311            buf_id++;
312        }
313        else if( vpn == vpn_max )  // last page: one single buffer
314        {
315            paddr = (((paddr_t)ppn) << 12);
316            count = offset_last;
317            cmd_table->entry[buf_id].dba  = (unsigned int)(paddr);
318            cmd_table->entry[buf_id].dbau = (unsigned int)(paddr >> 32);
319            cmd_table->entry[buf_id].dbc  = count;
320
321#if GIET_DEBUG_HBA_DRIVER
322_puts("\n- buf_index = ");
323_putd( buf_id );
324_puts(" / paddr = ");
325_putl( paddr );
326_puts(" / count = ");
327_putd( count );
328_puts("\n");
329#endif
330            buf_id++;
331        }
332        else if( offset )          // midle page and offset != 0: two buffers 
333        {
334            paddr = (((paddr_t)ppn) << 12);
335           
336            count = offset;
337            cmd_table->entry[buf_id].dba  = (unsigned int)(paddr);
338            cmd_table->entry[buf_id].dbau = (unsigned int)(paddr >> 32);
339            cmd_table->entry[buf_id].dbc  = count;
340
341#if GIET_DEBUG_HBA_DRIVER
342_puts("\n- buf_index = ");
343_putd( buf_id );
344_puts(" / paddr = ");
345_putl( paddr );
346_puts(" / count = ");
347_putd( count );
348_puts("\n");
349#endif
350            buf_id++;
351
352            paddr = (((paddr_t)ppn) << 12) + offset;
353            count = 0x1000 - offset; 
354            cmd_table->entry[buf_id].dba  = (unsigned int)(paddr);
355            cmd_table->entry[buf_id].dbau = (unsigned int)(paddr >> 32);
356            cmd_table->entry[buf_id].dbc  = count;
357
358#if GIET_DEBUG_HBA_DRIVER
359_puts("\n- buf_index = ");
360_putd( buf_id );
361_puts(" / paddr = ");
362_putl( paddr );
363_puts(" / count = ");
364_putd( count );
365_puts("\n");
366#endif
367            buf_id++;
368        }
369        else                      // middle page and offset == 0: one buffer
370        {
371            paddr = (((paddr_t)ppn) << 12);
372            count = 0x1000; 
373            cmd_table->entry[buf_id].dba  = (unsigned int)(paddr);
374            cmd_table->entry[buf_id].dbau = (unsigned int)(paddr >> 32);
375            cmd_table->entry[buf_id].dbc  = count;
376
377#if GIET_DEBUG_HBA_DRIVER
378_puts("\n- buf_index = ");
379_putd( buf_id );
380_puts(" / paddr = ");
381_putl( paddr );
382_puts(" / count = ");
383_putd( count );
384_puts("\n");
385#endif
386            buf_id++;
387        }
388    }
389
390    // initialize command table header
391    cmd_table->header.lba0 = (char)lba;
392    cmd_table->header.lba1 = (char)(lba>>8);
393    cmd_table->header.lba2 = (char)(lba>>16);
394    cmd_table->header.lba3 = (char)(lba>>24);
395
396    // initialise command descriptor
397    cmd_desc->prdtl[0] = (unsigned char)(buf_id);
398    cmd_desc->prdtl[1] = (unsigned char)(buf_id>>8);
399    cmd_desc->ctba     = (unsigned int)(hba_cmd_table_paddr[channel_id][cmd_id]);
400    cmd_desc->ctbau    = (unsigned int)(hba_cmd_table_paddr[channel_id][cmd_id]>>32);
401    if( is_read ) cmd_desc->flag[0] = 0x00;
402    else          cmd_desc->flag[0] = 0x40;     
403   
404    // update PXCI register
405    hba_address[HBA_PXCI] = (1<<cmd_id);
406
407    // update command pointer
408    hba_cmd_slot[channel_id] = (cmd_id + 1)%32;
409
410    return  0;
411} 
412///////////////////////////////////////////////////////////////////
413// Register a write command in Command List and Command Table
414// for a single buffer.
415// Returns 0 if success, > 0 if error.
416///////////////////////////////////////////////////////////////////
417unsigned int _hba_write( unsigned int  mode,
418                         unsigned int  lba,
419                         void*         buffer, 
420                         unsigned int  count )
421{
422    return _hba_cmd_set( 0, lba, (unsigned int)buffer, count );
423}
424
425///////////////////////////////////////////////////////////////////
426// Register a read command in Command List and Command Table
427// for a single buffer.
428// Returns 0 if success, > 0 if error.
429///////////////////////////////////////////////////////////////////
430unsigned int _hba_read( unsigned int  mode,
431                        unsigned int  lba, 
432                        void*         buffer, 
433                        unsigned int  count )
434{
435    return _hba_cmd_set( 1, lba, (unsigned int)buffer, count );
436}
437//////////////////////////////////////////////////////////////////
438// This function initializes for a given channel
439// - the HBA hardware registers,
440// - the command list pointer,
441// - the command lists physical addresse,
442// - the command tables physical addresses array,
443//////////////////////////////////////////////////////////////////
444unsigned int _hba_init( unsigned int channel )
445{
446    unsigned int ppn;
447    unsigned int flags;
448    unsigned int fail;
449    unsigned int vbase;
450    unsigned int c;               // c == command index
451
452    // get page_table pointer
453    unsigned int pt = _get_context_slot(CTX_PTAB_ID);
454
455    // HBA registers
456    unsigned int*  hba_address;
457    hba_address = (unsigned int*)&seg_ioc_base + HBA_SPAN * channel;
458
459    hba_address[HBA_PXCLB]  = (unsigned int)(&hba_cmd_list[channel]);
460    hba_address[HBA_PXCLBU] = 0;
461    hba_address[HBA_PXIE]   = 0x40000001;
462    hba_address[HBA_PXIS]   = 0;
463    hba_address[HBA_PXCI]   = 0;
464    hba_address[HBA_PXCMD]  = 1;
465
466    // command list pointer       
467    hba_cmd_slot[channel] = 0;
468
469    // Command list physical addresse
470    vbase = (unsigned int)(&hba_cmd_list[channel]);
471    fail = _v2p_translate( (page_table_t*)pt,
472                           vbase>>12,
473                           &ppn,
474                           &flags );
475    if ( fail )
476    {
477        _tty_get_lock( 0 );
478        _puts("[GIET ERROR] in _hba_init() : command list unmapped\n");
479        _tty_release_lock( 0 );
480        return 1;
481    }
482    hba_cmd_list_paddr[channel] = ((paddr_t)ppn) | (vbase & 0xFFF);
483
484    // Command tables physical addresses
485    for( c=0 ; c<32 ; c++ )
486    {
487        vbase = (unsigned int)(&hba_cmd_table[channel][c]);
488        fail = _v2p_translate( (page_table_t*)pt,
489                               vbase>>12,
490                               &ppn,
491                               &flags );
492        if ( fail )
493        {
494            _tty_get_lock( 0 );
495            _puts("[GIET ERROR] in _hba_init() : command table unmapped\n");
496            _tty_release_lock( 0 );
497            return 1;
498        }
499        hba_cmd_table_paddr[channel][c] = ((paddr_t)ppn) | (vbase & 0xFFF);
500    }
501
502    return 0;
503}
504
505///////////////////////////////////////////////////////////////////////////////
506//     _hba_get_block_size()
507// This function returns the block_size of HBA controller
508///////////////////////////////////////////////////////////////////////////////
509unsigned int _hba_get_block_size()
510{
511    // TODO The block size must be obtained from the hardware...
512    return 512;
513}
514
515
516// Local Variables:
517// tab-width: 4
518// c-basic-offset: 4
519// c-file-offsets:((innamespace . 0)(inline-open . 0))
520// indent-tabs-mode: nil
521// End:
522// vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
523
Note: See TracBrowser for help on using the repository browser.