/* * fatfs.c - FATFS file system API implementation. * * Author Mohamed Lamine Karaoui (2014,2015) * Alain Greiner (2016,2017) * * Copyright (c) UPMC Sorbonne Universites * * This file is part of ALMOS-MKH. * * ALMOS-MKH is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2.0 of the License. * * ALMOS-MKH is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ALMOS-MKH; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include ////////////////////////////////////////////////////////////////////////////////////////// // Extern variables ////////////////////////////////////////////////////////////////////////////////////////// extern vfs_ctx_t fs_context[FS_TYPES_NR]; // allocated in vfs.c file extern remote_barrier_t global_barrier; // allocated dans kernel_init.c ////////////////////////////////////////////////////////////////////////////////////////// // FATFS specific functions : these functions cannot be called by the VFS ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////// inline uint32_t fatfs_lba_from_cluster( fatfs_ctx_t * ctx, uint32_t cluster ) { return (ctx->cluster_begin_lba + ((cluster - 2) << 3)); } ///////////////////////////////////////////// error_t fatfs_get_cluster( mapper_t * mapper, uint32_t first_cluster, uint32_t searched_page, uint32_t * cluster ) { page_t * current_page_desc; // pointer on current page descriptor uint32_t * current_page_buffer; // pointer on current page (array of uint32_t) uint32_t current_page_index; // index of current page in mapper uint32_t current_page_offset; // offset of slot in current page uint32_t page_count_in_file; // index of page in file (index in linked list) uint32_t current_cluster; // content of current FAT slot // compute number of FAT slots per PPM page uint32_t slots_per_page = CONFIG_PPM_PAGE_SIZE >> 2; // initialize loop variable current_page_index = first_cluster / slots_per_page; current_page_offset = first_cluster % slots_per_page; page_count_in_file = 0; // scan FAT (i.e. traverse FAT linked list) while( page_count_in_file <= searched_page ) { // get pointer on current page descriptor current_page_desc = mapper_get_page( mapper , current_page_index ); if( current_page_desc == NULL ) return EIO; // get pointer on buffer for current page current_page_buffer = (uint32_t *)ppm_page2base( current_page_desc ); // get FAT slot content current_cluster = current_page_buffer[current_page_offset]; // update loop variables current_page_index = current_cluster / slots_per_page; current_page_offset = current_cluster % slots_per_page; page_count_in_file++; } // return success *cluster = current_cluster; return 0; } // end fatfs_get_cluster() /////////////////////////////////////////////////////////////////////////////////////// // This static function return an integer record value (one, two, or four bytes) // from a memory buffer, taking into account endianness. /////////////////////////////////////////////////////////////////////////////////////// // @ offset : first byte of record in buffer. // @ size : record length in bytes (1/2/4). // @ buffer : pointer on buffer base. // @ little endian : the most significant byte has the highest address when true. // @ return the integer value in a 32 bits word. /////////////////////////////////////////////////////////////////////////////////////// static uint32_t get_record_from_buffer( uint32_t offset, uint32_t size, uint8_t * buffer, uint32_t little_endian ) { uint32_t n; uint32_t res = 0; if ( little_endian) { for( n = size ; n > 0 ; n-- ) res = (res<<8) | buffer[offset+n-1]; } else { for( n = 0 ; n < size ; n++ ) res = (res<<8) | buffer[offset+n]; } return res; } // end get_record_from_buffer() //////////////////////////////////////////////////////////////////////////////////////// // This function returns the FATFS cluster index of a page identified by its page // index in the file, using the FAT mapper. It scans the FAT mapper, starting from the // FATFS cluster index allocated to the first page of the file, until it reaches the // searched page. The FAT mapper is automatically updated in case of miss. // This function can be called by any thread running in any cluster, as it uses the // RPC_FATFS_GET_CLUSTER to access the remote FAT mapper if required. // We use a RPC to scan the FAT because the RPC_FIFO will avoid contention // in the cluster containing the FAT mapper, and the RPC latency is not critical // compared to the device access latency. //////////////////////////////////////////////////////////////////////////////////////// // @ ctx : pointer on local FATFS context. // @ first_cluster : first cluster allocated to a file in FATFS. // @ page_index : index of searched page in file (one page occupies one cluster). // @ cluster_index : [out] pointer on buffer for FATFS cluster index. // @ return 0 if success / return EIO if a FAT cluster miss cannot be solved. //////////////////////////////////////////////////////////////////////////////////////// static error_t fatfs_cluster_from_index( fatfs_ctx_t * ctx, uint32_t first_cluster, uint32_t page_index, uint32_t * cluster_index ) { uint32_t searched_cluster; // searched FATFS cluster index error_t error; // get extended pointer on FAT mapper xptr_t fat_mapper_xp = ctx->fat_mapper_xp; // get cluster cxy and local pointer on FAT mapper cxy_t fat_mapper_cxy = GET_CXY( fat_mapper_xp ); mapper_t * fat_mapper_ptr = (mapper_t *)GET_PTR( fat_mapper_xp ); if( fat_mapper_cxy == local_cxy ) // FAT mapper is local { error = fatfs_get_cluster( fat_mapper_ptr, first_cluster, page_index, &searched_cluster ); } else // FAT mapper is remote { rpc_fatfs_get_cluster_client( fat_mapper_cxy, fat_mapper_ptr, first_cluster, page_index, &searched_cluster, &error ); } if( error ) { printk("\n[ERROR] in %s : cannot access FAT\n", __FUNCTION__ ); return error; } // return success *cluster_index = searched_cluster; return 0; } // end fatfs_cluster_from_index() /////////////////////////////////////////////////////////////////////////////////////// // The following functions are called by the VFS. /////////////////////////////////////////////////////////////////////////////////////// /////////////////// xptr_t fatfs_init() { kmem_req_t req; fatfs_ctx_t * fatfs_ctx; // local pointer on FATFS context vfs_ctx_t * vfs_ctx; // local pointer on VFS context xptr_t root_inode_xp; // extended pointer on root inode error_t error; // get local pointer on VFS context for FATFS vfs_ctx = &fs_context[FS_TYPE_FATFS]; // get number of kernel instances and extended pointer on global barrier cluster_t * cluster = LOCAL_CLUSTER; uint32_t nb_clusters = cluster->x_size * cluster->y_size; xptr_t barrier_xp = XPTR( cluster->io_cxy , &global_barrier ); ///// step 1 : all clusters allocate memory for FATFS context // allocate memory for FATFS context extension req.type = KMEM_FATFS_CTX; req.size = sizeof(fatfs_ctx_t); req.flags = AF_KERNEL | AF_ZERO; fatfs_ctx = (fatfs_ctx_t *)kmem_alloc( &req ); if( fatfs_ctx == NULL ) { printk("\n[PANIC] in %s : no memory for FATFS context\n", __FUNCTION__ ); hal_core_sleep(); } ///// step 2 : only cluster_0 access device and creates root inode if( local_cxy == 0 ) { // create VFS root inode error = vfs_inode_create( XPTR_NULL, // no parent dentry FS_TYPE_FATFS, INODE_TYPE_DIR, 0, // attr 0, // rights 0, // uid 0, // gid &root_inode_xp ); assert( (error == 0 ) , __FUNCTION__ , "cannot create VFS root inode" ); // initialize VFS context / access device to initialize FATFS context error = fatfs_ctx_init( vfs_ctx, fatfs_ctx, root_inode_xp ); // create FATFS root inode error = fatfs_inode_create( GET_PTR( root_inode_xp ) , fatfs_ctx->root_dir_cluster ); if( error ) { printk("\n[PANIC] in %s : cannot create FATFS root inode\n", __FUNCTION__ ); hal_core_sleep(); } } //////////////// synchronize all clusters remote_barrier( barrier_xp , nb_clusters ); ///// step 3 : all others clusters initialize both context and extension if( local_cxy != 0 ) { // copy VFS context from remote cluster_0 to local cluster hal_remote_memcpy( XPTR( local_cxy , vfs_ctx ), XPTR( 0 , vfs_ctx ), sizeof(vfs_ctx_t) ); // copy FATFS context from remote cluster_0 to local cluster hal_remote_memcpy( XPTR( local_cxy , fatfs_ctx ), XPTR( 0 , vfs_ctx->extend ) , sizeof(fatfs_ctx_t) ); // update extend field in local copy of VFS context vfs_ctx->extend = fatfs_ctx; } return root_inode_xp; } // end fatfs_init() ////////////////////////////////////////////// error_t fatfs_ctx_init( vfs_ctx_t * vfs_ctx, fatfs_ctx_t * fatfs_ctx, xptr_t root_inode_xp ) { error_t error; uint8_t buffer[512]; // buffer for boot record // make a synchronous access to IOC device to read the boot record from device error = dev_ioc_sync_read( buffer , 0 , 1 ); assert( (error == 0) , __FUNCTION__ , "cannot access FAT boot record" ); // check sector size from boot record uint32_t sector_size = get_record_from_buffer( BPB_BYTSPERSEC , buffer , 1 ); assert( (sector_size == 512) , __FUNCTION__ , "sector size must be 512 bytes" ); // check cluster size from boot record uint32_t nb_sectors = get_record_from_buffer( BPB_SECPERCLUS , buffer , 1 ); assert( (nb_sectors == 8) , __FUNCTION__ , "cluster size must be 8 sectors" ); // check number of FAT copies from boot record uint32_t nb_fats = get_record_from_buffer( BPB_NUMFATS , buffer , 1 ); assert( (nb_fats == 1) , __FUNCTION__ , "number of FAT copies must be 1" ); // get & check number of sectors in FAT from boot record uint32_t fat_sectors = get_record_from_buffer( BPB_FAT32_FATSZ32 , buffer , 1 ); assert( ((fat_sectors & 0xF) == 0) , __FUNCTION__ , "FAT not multiple of 16 sectors"); // get and check root cluster from boot record uint32_t root_cluster = get_record_from_buffer( BPB_FAT32_ROOTCLUS , buffer , 1 ); assert( (root_cluster == 2) , __FUNCTION__ , "Root cluster index must be 2"); // get FAT lba from boot record uint32_t fat_lba = get_record_from_buffer( BPB_RSVDSECCNT , buffer , 1 ); // allocate a mapper for the FAT itself mapper_t * fat_mapper = mapper_create(); assert( (fat_mapper != NULL) , __FUNCTION__ , "no memory for FAT mapper" ); // initialize the FATFS context fatfs_ctx->fat_begin_lba = fat_lba; fatfs_ctx->fat_sectors_count = fat_sectors; fatfs_ctx->bytes_per_sector = sector_size; fatfs_ctx->bytes_per_cluster = sector_size * nb_sectors; fatfs_ctx->cluster_begin_lba = fat_lba + fat_sectors; fatfs_ctx->root_dir_cluster = 2; fatfs_ctx->last_allocated_sector = 0; // TODO ??? fatfs_ctx->last_allocated_index = 0; // TODO ??? fatfs_ctx->fat_mapper_xp = XPTR( local_cxy , fat_mapper ); // initialize the VFS context vfs_ctx->type = FS_TYPE_FATFS; vfs_ctx->attr = 0; // not READ_ONLY / not SYNC vfs_ctx->count = fat_sectors << 10; // total number of sectors in data region vfs_ctx->blksize = 512; // number of bytes per sector vfs_ctx->root_xp = root_inode_xp; vfs_ctx->extend = fatfs_ctx; spinlock_init( &vfs_ctx->lock ); bitmap_init( vfs_ctx->bitmap , CONFIG_VFS_MAX_INODES ); return 0; } // end fatfs_ctx_init() //////////////////////////////////////////////////// void fatfs_ctx_destroy( struct vfs_ctx_s * vfs_ctx ) { kmem_req_t req; fatfs_ctx_t * fatfs_ctx; // get pointer on FATFS context extension fatfs_ctx = (fatfs_ctx_t *)vfs_ctx->extend; req.type = KMEM_FATFS_INODE; req.ptr = fatfs_ctx; kmem_free( &req ); } //////////////////////////////////////////////////// error_t fatfs_inode_create( vfs_inode_t * vfs_inode, uint32_t first_cluster ) { kmem_req_t req; fatfs_inode_t * fatfs_inode; // allocate memory for FATFS inode extension req.type = KMEM_FATFS_INODE; req.size = sizeof(fatfs_inode_t); req.flags = AF_KERNEL | AF_ZERO; fatfs_inode = (fatfs_inode_t *)kmem_alloc( &req ); if( fatfs_inode == NULL ) return ENOMEM; // link FATFS inode to VFS inode vfs_inode->extend = fatfs_inode; // initialise FATFS inode fatfs_inode->first_cluster = first_cluster; return 0; } /////////////////////////////////////////////////// void fatfs_inode_destroy( vfs_inode_t * vfs_inode ) { kmem_req_t req; fatfs_inode_t * fatfs_inode; // get pointer on FATFS inode fatfs_inode = (fatfs_inode_t *)vfs_inode->extend; req.type = KMEM_FATFS_INODE; req.ptr = fatfs_inode; kmem_free( &req ); vfs_inode->extend = NULL; } //////////////////////////////////////////////// static error_t fatfs_access_page( page_t * page, bool_t is_read ) { // get memory buffer base address uint8_t * buffer = (uint8_t *)ppm_page2base( page ); // get pointer on source mapper and page index from page descriptor mapper_t * mapper = page->mapper; uint32_t page_index = page->index; // get VFS inode pointer from mapper vfs_inode_t * vfs_inode = mapper->inode; // get FATFS inode pointer for VFS inode fatfs_inode_t * fatfs_inode = (fatfs_inode_t *)vfs_inode->extend; // get first cluster index from FATFS inode uint32_t first_cluster = fatfs_inode->first_cluster; // get FATFS context pointer from FATFS inode fatfs_ctx_t * fatfs_ctx = (fatfs_ctx_t *)vfs_inode->ctx->extend; // get number of sectors uint32_t count = fatfs_ctx->sectors_per_cluster; // compute FATFS_cluster index for the accessed page uint32_t cluster = 0; error_t error = fatfs_cluster_from_index( fatfs_ctx, first_cluster, page_index, &cluster ); if( error ) return EIO; // get lba from cluster uint32_t lba = fatfs_lba_from_cluster( fatfs_ctx , cluster ); // access device if( is_read ) error = dev_ioc_read ( buffer , lba , count ); else error = dev_ioc_write( buffer , lba , count ); if( error ) { printk("\n[ERROR] in %s : cannot access IOC device\n", __FUNCTION__ ); return error; } // successful access return 0; } //////////////////////////////////////////////// error_t fatfs_write_page( struct page_s * page ) { bool_t is_read = false; return fatfs_access_page( page , is_read ); } /////////////////////////////////////////////// error_t fatfs_read_page( struct page_s * page ) { bool_t is_read = true; return fatfs_access_page( page , is_read ); }