source: trunk/kernel/vfs/fatfs.c @ 23

Last change on this file since 23 was 23, checked in by alain, 5 years ago

Introduce syscalls.

File size: 17.2 KB
Line 
1/*
2 * fatfs.c - FATFS file system API implementation.
3 *
4 * Author    Mohamed Lamine Karaoui (2014,2015)
5 *           Alain Greiner (2016,2017)
6 *
7 * Copyright (c) UPMC Sorbonne Universites
8 *
9 * This file is part of ALMOS-MKH.
10 *
11 * ALMOS-MKH is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; version 2.0 of the License.
14 *
15 * ALMOS-MKH is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25
26#include <hal_types.h>
27#include <hal_special.h>
28#include <printk.h>
29#include <kmem.h>
30#include <ppm.h>
31#include <vfs.h>
32#include <rpc.h>
33#include <mapper.h>
34#include <cluster.h>
35#include <dev_ioc.h>
36#include <fatfs.h>
37
38//////////////////////////////////////////////////////////////////////////////////////////
39//          Extern  variables         
40//////////////////////////////////////////////////////////////////////////////////////////
41
42extern vfs_ctx_t        fs_context[FS_TYPES_NR];   // allocated in vfs.c file
43
44extern remote_barrier_t global_barrier;            // allocated dans kernel_init.c
45 
46//////////////////////////////////////////////////////////////////////////////////////////
47// FATFS specific functions : these functions cannot be called by the VFS
48//////////////////////////////////////////////////////////////////////////////////////////
49
50//////////////////////////////////////////////////////////
51inline uint32_t fatfs_lba_from_cluster( fatfs_ctx_t * ctx,
52                                        uint32_t      cluster )
53{
54    return (ctx->cluster_begin_lba + ((cluster - 2) << 3));
55}
56
57/////////////////////////////////////////////
58error_t fatfs_get_cluster( mapper_t * mapper,
59                           uint32_t   first_cluster,
60                           uint32_t   searched_page,
61                           uint32_t * cluster )
62{
63    page_t   * current_page_desc;      // pointer on current page descriptor
64    uint32_t * current_page_buffer;    // pointer on current page (array of uint32_t)
65    uint32_t   current_page_index;     // index of current page in mapper
66    uint32_t   current_page_offset;    // offset of slot in current page
67    uint32_t   page_count_in_file;     // index of page in file (index in linked list)
68    uint32_t   current_cluster;        // content of current FAT slot
69
70    // compute number of FAT slots per PPM page
71    uint32_t slots_per_page = CONFIG_PPM_PAGE_SIZE >> 2;
72
73    // initialize loop variable
74    current_page_index  = first_cluster / slots_per_page;
75    current_page_offset = first_cluster % slots_per_page;
76    page_count_in_file  = 0;
77
78    // scan FAT (i.e. traverse FAT linked list)
79    while( page_count_in_file <= searched_page )
80    {
81        // get pointer on current page descriptor
82        current_page_desc = mapper_get_page( mapper , current_page_index );
83
84        if( current_page_desc == NULL ) return EIO;
85
86        // get pointer on buffer for current page
87        current_page_buffer = (uint32_t *)ppm_page2base( current_page_desc );
88
89        // get FAT slot content
90        current_cluster = current_page_buffer[current_page_offset];
91
92        // update loop variables
93        current_page_index  = current_cluster / slots_per_page;
94        current_page_offset = current_cluster % slots_per_page;
95        page_count_in_file++;
96    }
97   
98    // return success
99    *cluster = current_cluster;
100    return 0;
101
102}  // end fatfs_get_cluster()
103
104///////////////////////////////////////////////////////////////////////////////////////
105// This static function return an integer record value (one, two, or four bytes)
106// from a memory buffer, taking into account endianness.
107///////////////////////////////////////////////////////////////////////////////////////
108// @ offset        : first byte of record in buffer.
109// @ size          : record length in bytes (1/2/4).
110// @ buffer        : pointer on buffer base.
111// @ little endian : the most significant byte has the highest address when true.
112// @ return the integer value in a 32 bits word.
113///////////////////////////////////////////////////////////////////////////////////////
114static uint32_t get_record_from_buffer( uint32_t    offset,
115                                        uint32_t    size,
116                                        uint8_t   * buffer,
117                                        uint32_t    little_endian )
118{
119    uint32_t n;
120    uint32_t res  = 0;
121
122    if ( little_endian)
123    {
124        for( n = size ; n > 0 ; n-- ) res = (res<<8) | buffer[offset+n-1];
125    }
126    else
127    {
128        for( n = 0 ; n < size ; n++ ) res = (res<<8) | buffer[offset+n];
129    }
130    return res;
131
132}  // end get_record_from_buffer()
133
134
135
136////////////////////////////////////////////////////////////////////////////////////////
137// This function returns the FATFS cluster index of a page identified by its page
138// index in the file, using the FAT mapper. It scans the FAT mapper, starting from the
139// FATFS cluster index allocated to the first page of the file, until it reaches the
140// searched page. The FAT mapper is automatically updated in case of miss.
141// This function can be called by any thread running in any cluster, as it uses the
142// RPC_FATFS_GET_CLUSTER to access the remote FAT mapper if required.
143// We use a RPC to scan the FAT because the RPC_FIFO will avoid contention
144// in the cluster containing the FAT mapper, and the RPC latency is not critical
145// compared to the device access latency.
146////////////////////////////////////////////////////////////////////////////////////////
147// @ ctx               : pointer on local FATFS context.
148// @ first_cluster : first cluster allocated to a file in FATFS.
149// @ page_index    : index of searched page in file (one page occupies one cluster).
150// @ cluster_index : [out] pointer on buffer for FATFS cluster index.
151// @ return 0 if success / return EIO if a FAT cluster miss cannot be solved.
152////////////////////////////////////////////////////////////////////////////////////////
153static error_t fatfs_cluster_from_index( fatfs_ctx_t * ctx,
154                                         uint32_t      first_cluster,
155                                         uint32_t      page_index,
156                                         uint32_t    * cluster_index )
157{
158    uint32_t searched_cluster;   // searched FATFS cluster index
159    error_t  error;
160
161    // get extended pointer on FAT mapper
162    xptr_t fat_mapper_xp = ctx->fat_mapper_xp;
163
164    // get cluster cxy and local pointer on FAT mapper
165    cxy_t      fat_mapper_cxy = GET_CXY( fat_mapper_xp );
166    mapper_t * fat_mapper_ptr = (mapper_t *)GET_PTR( fat_mapper_xp );
167
168    if( fat_mapper_cxy == local_cxy )    // FAT mapper is local
169    {
170        error = fatfs_get_cluster( fat_mapper_ptr,
171                                   first_cluster,
172                                   page_index,
173                                   &searched_cluster );
174    }
175    else                                 // FAT mapper is remote
176    {
177        rpc_fatfs_get_cluster_client( fat_mapper_cxy,
178                                      fat_mapper_ptr,
179                                      first_cluster,
180                                      page_index,
181                                      &searched_cluster,
182                                      &error );
183    }
184   
185    if( error )
186    {
187        printk("\n[ERROR] in %s : cannot access FAT\n", __FUNCTION__ );
188        return error;
189    }
190
191    // return success
192    *cluster_index = searched_cluster;
193    return 0;
194
195}  // end fatfs_cluster_from_index()
196
197
198
199
200///////////////////////////////////////////////////////////////////////////////////////
201//          The following functions are called by the VFS.
202///////////////////////////////////////////////////////////////////////////////////////
203
204
205///////////////////
206xptr_t fatfs_init()
207{
208    kmem_req_t    req;
209    fatfs_ctx_t * fatfs_ctx;       // local pointer on FATFS context
210    vfs_ctx_t   * vfs_ctx;         // local pointer on VFS context
211    xptr_t        root_inode_xp;   // extended pointer on root inode
212    error_t       error;
213
214    // get local pointer on VFS context for FATFS
215    vfs_ctx = &fs_context[FS_TYPE_FATFS];
216
217    // get number of kernel instances and extended pointer on global barrier
218    cluster_t * cluster     = LOCAL_CLUSTER;
219    uint32_t    nb_clusters = cluster->x_size * cluster->y_size;
220    xptr_t      barrier_xp  = XPTR( cluster->io_cxy , &global_barrier );
221
222    ///// step 1 : all clusters allocate memory for FATFS context
223
224    // allocate memory for FATFS context extension
225        req.type    = KMEM_FATFS_CTX;
226        req.size    = sizeof(fatfs_ctx_t);
227    req.flags   = AF_KERNEL | AF_ZERO;
228        fatfs_ctx   = (fatfs_ctx_t *)kmem_alloc( &req );
229
230    if( fatfs_ctx == NULL ) 
231    {
232        printk("\n[PANIC] in %s : no memory for FATFS context\n", __FUNCTION__ );
233        hal_core_sleep();
234    }
235   
236    ///// step 2 : only cluster_0 access device and creates root inode
237
238    if( local_cxy == 0 )
239    {
240        // create VFS root inode
241        error = vfs_inode_create( XPTR_NULL,        // no parent dentry
242                                  FS_TYPE_FATFS,
243                                  INODE_TYPE_DIR,
244                                  0,                // attr
245                                  0,                // rights
246                                  0,                // uid
247                                  0,                // gid
248                                  &root_inode_xp );
249
250        assert( (error == 0 ) , __FUNCTION__ , "cannot create VFS root inode" );
251
252        // initialize VFS context / access device to initialize FATFS context
253        error = fatfs_ctx_init( vfs_ctx,
254                                fatfs_ctx,
255                                root_inode_xp );
256
257        // create FATFS root inode
258        error = fatfs_inode_create( GET_PTR( root_inode_xp ) , 
259                                    fatfs_ctx->root_dir_cluster );
260
261        if( error )
262        {
263            printk("\n[PANIC] in %s : cannot create FATFS root inode\n", __FUNCTION__ );
264            hal_core_sleep();
265        }
266
267    }
268
269    //////////////// synchronize all clusters
270    remote_barrier( barrier_xp , nb_clusters );
271
272    ///// step 3 : all others clusters initialize both context and extension
273
274    if( local_cxy != 0 )
275    {
276        // copy VFS context from remote cluster_0 to local cluster
277        hal_remote_memcpy( XPTR( local_cxy , vfs_ctx ), 
278                           XPTR( 0 , vfs_ctx ),
279                           sizeof(vfs_ctx_t) );
280
281        // copy FATFS context from remote cluster_0 to local cluster
282        hal_remote_memcpy( XPTR( local_cxy , fatfs_ctx ), 
283                           XPTR( 0 , vfs_ctx->extend ) ,
284                           sizeof(fatfs_ctx_t) );
285
286        // update extend field in local copy of VFS context
287        vfs_ctx->extend = fatfs_ctx;
288    }
289
290    return root_inode_xp;
291
292}  // end fatfs_init()
293
294//////////////////////////////////////////////
295error_t fatfs_ctx_init( vfs_ctx_t   * vfs_ctx,
296                        fatfs_ctx_t * fatfs_ctx,
297                        xptr_t        root_inode_xp )
298{
299    error_t  error;
300    uint8_t  buffer[512];    // buffer for boot record
301
302    // make a synchronous access to IOC device to read the boot record from device
303    error = dev_ioc_sync_read( buffer , 0 , 1 );
304    assert( (error == 0) , __FUNCTION__ , "cannot access FAT boot record" );
305
306    // check sector size from boot record
307    uint32_t sector_size = get_record_from_buffer( BPB_BYTSPERSEC , buffer , 1 );
308    assert( (sector_size == 512) , __FUNCTION__ , "sector size must be 512 bytes" );
309
310    // check cluster size from boot record
311    uint32_t nb_sectors = get_record_from_buffer( BPB_SECPERCLUS , buffer , 1 );
312    assert( (nb_sectors == 8) , __FUNCTION__ , "cluster size must be 8 sectors" );
313
314    // check number of FAT copies from boot record
315    uint32_t nb_fats = get_record_from_buffer( BPB_NUMFATS , buffer , 1 );
316    assert( (nb_fats == 1) , __FUNCTION__ , "number of FAT copies must be 1" );
317
318    // get & check number of sectors in FAT from boot record
319    uint32_t fat_sectors = get_record_from_buffer( BPB_FAT32_FATSZ32 , buffer , 1 );
320    assert( ((fat_sectors & 0xF) == 0) , __FUNCTION__ , "FAT not multiple of 16 sectors");
321
322    // get and check root cluster from boot record
323    uint32_t root_cluster = get_record_from_buffer( BPB_FAT32_ROOTCLUS , buffer , 1 );
324    assert( (root_cluster == 2) , __FUNCTION__ , "Root cluster index must be  2");
325
326    // get FAT lba from boot record
327    uint32_t fat_lba = get_record_from_buffer( BPB_RSVDSECCNT , buffer , 1 );
328   
329    // allocate a mapper for the FAT itself
330    mapper_t * fat_mapper = mapper_create();
331    assert( (fat_mapper != NULL) , __FUNCTION__ , "no memory for FAT mapper" );
332
333    // initialize the FATFS context
334    fatfs_ctx->fat_begin_lba         = fat_lba;
335    fatfs_ctx->fat_sectors_count     = fat_sectors; 
336    fatfs_ctx->bytes_per_sector      = sector_size;
337    fatfs_ctx->bytes_per_cluster     = sector_size * nb_sectors;
338    fatfs_ctx->cluster_begin_lba     = fat_lba + fat_sectors;
339    fatfs_ctx->root_dir_cluster      = 2;
340    fatfs_ctx->last_allocated_sector = 0;    // TODO ???
341    fatfs_ctx->last_allocated_index  = 0;    // TODO ???
342    fatfs_ctx->fat_mapper_xp         = XPTR( local_cxy , fat_mapper );
343
344    // initialize the VFS context
345    vfs_ctx->type    = FS_TYPE_FATFS;
346    vfs_ctx->attr    = 0;                    // not READ_ONLY / not SYNC
347    vfs_ctx->count   = fat_sectors << 10;    // total number of sectors in data region
348    vfs_ctx->blksize = 512;                  // number of bytes per sector
349    vfs_ctx->root_xp = root_inode_xp;
350    vfs_ctx->extend  = fatfs_ctx;
351
352    spinlock_init( &vfs_ctx->lock );
353
354    bitmap_init( vfs_ctx->bitmap , CONFIG_VFS_MAX_INODES );
355
356    return 0;
357
358}  // end fatfs_ctx_init()
359 
360
361
362////////////////////////////////////////////////////
363void fatfs_ctx_destroy( struct vfs_ctx_s * vfs_ctx )
364{
365    kmem_req_t    req;
366    fatfs_ctx_t * fatfs_ctx;
367
368    // get pointer on FATFS context extension
369    fatfs_ctx = (fatfs_ctx_t *)vfs_ctx->extend;
370
371    req.type = KMEM_FATFS_INODE;
372    req.ptr  = fatfs_ctx;
373    kmem_free( &req );
374}
375
376
377////////////////////////////////////////////////////
378error_t fatfs_inode_create( vfs_inode_t * vfs_inode,
379                            uint32_t      first_cluster )
380{
381    kmem_req_t      req;
382    fatfs_inode_t * fatfs_inode;
383
384    // allocate memory for FATFS inode extension
385        req.type    = KMEM_FATFS_INODE;
386        req.size    = sizeof(fatfs_inode_t);
387    req.flags   = AF_KERNEL | AF_ZERO;
388        fatfs_inode = (fatfs_inode_t *)kmem_alloc( &req );
389
390    if( fatfs_inode == NULL ) return ENOMEM;
391
392    // link FATFS inode to VFS inode
393    vfs_inode->extend = fatfs_inode;
394
395    // initialise FATFS inode
396    fatfs_inode->first_cluster = first_cluster;
397 
398    return 0;
399}
400
401///////////////////////////////////////////////////
402void fatfs_inode_destroy( vfs_inode_t * vfs_inode )
403{
404    kmem_req_t      req;
405    fatfs_inode_t * fatfs_inode;
406
407    // get pointer on FATFS inode
408    fatfs_inode = (fatfs_inode_t *)vfs_inode->extend;
409
410    req.type = KMEM_FATFS_INODE;
411    req.ptr  = fatfs_inode;
412    kmem_free( &req );
413
414        vfs_inode->extend = NULL;
415}
416
417
418////////////////////////////////////////////////
419static error_t fatfs_access_page( page_t * page,
420                                  bool_t   is_read )
421{
422    // get memory buffer base address
423    uint8_t * buffer = (uint8_t *)ppm_page2base( page );
424 
425    // get pointer on source mapper and page index from page descriptor
426    mapper_t * mapper      = page->mapper;
427    uint32_t   page_index  = page->index;
428
429    // get VFS inode pointer from mapper
430    vfs_inode_t * vfs_inode = mapper->inode;
431
432    // get FATFS inode pointer for VFS inode
433    fatfs_inode_t * fatfs_inode = (fatfs_inode_t *)vfs_inode->extend;
434
435    // get first cluster index from FATFS inode
436    uint32_t  first_cluster = fatfs_inode->first_cluster;
437
438    // get FATFS context pointer from FATFS inode
439    fatfs_ctx_t * fatfs_ctx = (fatfs_ctx_t *)vfs_inode->ctx->extend;
440
441    // get number of sectors
442    uint32_t count = fatfs_ctx->sectors_per_cluster;
443
444    // compute FATFS_cluster index for the accessed page
445    uint32_t cluster = 0;
446    error_t  error = fatfs_cluster_from_index( fatfs_ctx,
447                                               first_cluster,
448                                               page_index,
449                                               &cluster );
450    if( error ) return EIO;
451
452    // get lba from cluster
453    uint32_t lba = fatfs_lba_from_cluster( fatfs_ctx , cluster );
454
455    // access device
456    if( is_read ) error = dev_ioc_read ( buffer , lba , count );
457    else          error = dev_ioc_write( buffer , lba , count );     
458
459    if( error )
460    {
461        printk("\n[ERROR] in %s : cannot access IOC device\n", __FUNCTION__ );
462        return error;
463    } 
464
465    // successful access
466    return 0;
467}
468
469////////////////////////////////////////////////
470error_t fatfs_write_page( struct page_s * page )
471{
472    bool_t is_read = false;
473    return fatfs_access_page( page , is_read );
474}
475
476///////////////////////////////////////////////
477error_t fatfs_read_page( struct page_s * page )
478{
479    bool_t is_read = true;
480    return fatfs_access_page( page , is_read );
481}
482
Note: See TracBrowser for help on using the repository browser.