/* * devfs.c - DEVFS File system API implementation. * * Author Mohamed Lamine Karaoui (2014,2015) * Alain Greiner (2016,2017) * * Copyright (c) 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 #include ////////////////////////////////////////////////////////////////////////////////////////// // Extern variables ////////////////////////////////////////////////////////////////////////////////////////// extern vfs_ctx_t fs_context[FS_TYPES_NR]; // allocated in vfs.c file extern remote_barrier_t global_barrier; // allocated in kernel_init.c extern chdev_directory_t chdev_dir; // allocated in kernel_init.c //////////////////////////////////////////////////////////////////////////////////////// // DEVFS private functions //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// // This function creates in the local cluster the dentry and the associated inode, // for a DEVFS directory (level 0 or level 1 in DEVFS tree). //////////////////////////////////////////////////////////////////////////////////////// // @ name : directory entry name. // @ parent_xp : extended pointer on parent inode. // @ inode_xp : [out] buffer for extended pointer on created inode. //////////////////////////////////////////////////////////////////////////////////////// static void devfs_create_directory( char * name, xptr_t parent_xp, xptr_t * inode_xp ) { error_t error; xptr_t new_dentry_xp; // extended pointer on created dentry xptr_t new_inode_xp; // extended pointer on created inode // get parent inode cluster and local pointer cxy_t parent_cxy = GET_CXY( parent_xp ); vfs_inode_t * parent_ptr = (vfs_inode_t *)GET_PTR( parent_xp ); // create vfs_dentry in cluster containing the parent inode if( parent_cxy == local_cxy ) { error = vfs_dentry_create( FS_TYPE_DEVFS, name, parent_ptr, &new_dentry_xp ); } else { rpc_vfs_dentry_create_client( parent_cxy, FS_TYPE_DEVFS, name, parent_ptr, &new_dentry_xp, &error ); } if ( error ) { printk("\n[PANIC] in %s : cannot create dentry for %s in cluster %x/n", __FUNCTION__ , name , local_cxy ); hal_core_sleep(); } // create vfs_inode in local cluster TODO define the 4 arguments below [AG] uint32_t attr = 0; uint32_t rights = 0; uid_t uid = 0; gid_t gid = 0; error = vfs_inode_create( new_dentry_xp, FS_TYPE_DEVFS, INODE_TYPE_DIR, attr, rights, uid, gid, &new_inode_xp ); if( error ) { printk("\n[PANIC] in %s : cannot create inode for %s in cluster %x/n", __FUNCTION__ , name , local_cxy ); hal_core_sleep(); } // return extended pointer on directory inode *inode_xp = new_inode_xp; } // end devfs_create_directory() //////////////////////////////////////////////////////////////////////////////////////// // This function creates in the local cluster the dentry and the associated inode, // for a chdev (level 2 in DEVFS tree). //////////////////////////////////////////////////////////////////////////////////////// // @ chdev : local pointer on local chdev. // @ name : directory entry name. // @ parent : local pointer on local parent inode. // @ inode_xp : [out] buffer for extended pointer on created inode. //////////////////////////////////////////////////////////////////////////////////////// static void devfs_register_chdev( chdev_t * chdev, char * name, vfs_inode_t * parent, xptr_t * inode_xp ) { error_t error; xptr_t new_dentry_xp; xptr_t new_inode_xp; devfs_dmsg("\n[INFO] %s : create dentry for %s\n", __FUNCTION__ , name ); // create vfs_dentry in local cluster error = vfs_dentry_create( FS_TYPE_DEVFS, name, parent, &new_dentry_xp ); if ( error ) { printk("\n[PANIC] in %s : cannot create dentry for %s in cluster %x/n", __FUNCTION__ , name , local_cxy ); hal_core_sleep(); } devfs_dmsg("\n[INFO] %s : create inode for %s\n", __FUNCTION__ , name ); // create vfs_inode in local cluster uint32_t attr = 0; uint32_t rights = 0; uid_t uid = 0; gid_t gid = 0; error = vfs_inode_create( new_dentry_xp, FS_TYPE_DEVFS, INODE_TYPE_DEV, attr, rights, uid, gid, &new_inode_xp ); if( error ) { printk("\n[PANIC] in %s : cannot create inode for %s in cluster %x/n", __FUNCTION__ , name , local_cxy ); hal_core_sleep(); } // return extended pointer on chdev inode *inode_xp = new_inode_xp; } // end devfs_register_chdev() /////////////////////////////////////////////////////////////////////////////////////// // Generic API : the following functions are called by the VFS, // and must be defined by all supported file systems. /////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////// //////////////////////////////////////////// error_t devfs_mount( xptr_t parent_inode_xp, char * devfs_root_name ) { assert( (CURRENT_CORE->lid == 0) , __FUNCTION__ , "only CP0 should do it" ); vfs_inode_t * parent_inode_ptr; cxy_t parent_inode_cxy; vfs_ctx_t * vfs_ctx; char node_name[16]; uint32_t channel; xptr_t root_inode_xp; xptr_t external_inode_xp; xptr_t internal_inode_xp; xptr_t chdev_inode_xp; chdev_t * chdev_ptr; // 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 ); // get VFS root inode cluster and local pointer parent_inode_cxy = GET_CXY( parent_inode_xp ); parent_inode_ptr = (vfs_inode_t *)GET_PTR( parent_inode_xp ); // get local pointer on VFS context for DEVFS vfs_ctx = &fs_context[FS_TYPE_DEVFS]; ///// step 1 : all clusters initialize local DEVFS context ///// devfs_ctx_init( vfs_ctx , parent_inode_xp ); ///// step 2 : cluster_0 creates DEVFS root ///// if( local_cxy == 0 ) { devfs_create_directory( devfs_root_name, parent_inode_xp, &root_inode_xp ); } // synchronize all clusters remote_barrier( barrier_xp , nb_clusters ); ///// step 3 : all clusters create "internal" directory and chdevs ///// // TODO check device existence : (chdev_xp != XPTR_NULL) in chdev_dir snprintf( node_name , 16 , "internal_%x" , local_cxy ); devfs_create_directory( node_name, root_inode_xp, &internal_inode_xp ); // create ICU chdev inode chdev_ptr = (chdev_t *)GET_PTR( chdev_dir.icu[local_cxy] ); devfs_register_chdev( chdev_ptr, "icu", (vfs_inode_t *)GET_PTR( internal_inode_xp ), &chdev_inode_xp ); // create MMC chdev inode chdev_ptr = (chdev_t *)GET_PTR( chdev_dir.mmc[local_cxy] ); devfs_register_chdev( chdev_ptr, "mmc", (vfs_inode_t *)GET_PTR( internal_inode_xp ), &chdev_inode_xp ); // create DMA chdev inodes (one DMA channel per core) for( channel = 0 ; channel < cluster->cores_nr ; channel++ ) { chdev_ptr = (chdev_t *)GET_PTR( chdev_dir.dma[channel] ); snprintf( node_name , 16 , "dma_%d" , channel ); devfs_register_chdev( chdev_ptr, node_name, (vfs_inode_t *)GET_PTR( internal_inode_xp ), &chdev_inode_xp ); } ///// step 4 : cluster_io creates "external" directory and chdevs ///// // TODO check device existence : (chdev_xp != XPTR_NULL) in chdev_dir if( local_cxy == cluster->io_cxy ) { devfs_create_directory( "external", root_inode_xp, &external_inode_xp ); // create IOB chdev inode chdev_ptr = (chdev_t *)GET_PTR( chdev_dir.iob ); devfs_register_chdev( chdev_ptr, "iob", (vfs_inode_t *)GET_PTR( external_inode_xp ), &chdev_inode_xp ); // create PIC chdev inode chdev_ptr = (chdev_t *)GET_PTR( chdev_dir.pic ); devfs_register_chdev( chdev_ptr, "pic", (vfs_inode_t *)GET_PTR( external_inode_xp ), &chdev_inode_xp ); // create TXT chdev inodes for( channel = 0 ; channel < CONFIG_MAX_TXT_CHANNELS ; channel++ ) { chdev_ptr = (chdev_t *)GET_PTR( chdev_dir.txt[channel] ); snprintf( node_name , 16 , "txt_%d" , channel ); devfs_register_chdev( chdev_ptr, node_name, (vfs_inode_t *)GET_PTR( external_inode_xp ), &chdev_inode_xp ); } // create IOC chdev inodes for( channel = 0 ; channel < CONFIG_MAX_IOC_CHANNELS ; channel++ ) { chdev_ptr = (chdev_t *)GET_PTR( chdev_dir.ioc[channel] ); snprintf( node_name , 16 , "ioc_%d" , channel ); devfs_register_chdev( chdev_ptr, node_name, (vfs_inode_t *)GET_PTR( external_inode_xp ), &chdev_inode_xp ); } // create FBF chdev inodes for( channel = 0 ; channel < CONFIG_MAX_IOC_CHANNELS ; channel++ ) { chdev_ptr = (chdev_t *)GET_PTR( chdev_dir.ioc[channel] ); snprintf( node_name , 16 , "fbf_%d" , channel ); devfs_register_chdev( chdev_ptr, node_name, (vfs_inode_t *)GET_PTR( external_inode_xp ), &chdev_inode_xp ); } // create NIC_RX chdevs for( channel = 0 ; channel < CONFIG_MAX_NIC_CHANNELS ; channel++ ) { chdev_ptr = (chdev_t *)GET_PTR( chdev_dir.nic_rx[channel] ); snprintf( node_name , 16 , "nic_rx_%d" , channel ); devfs_register_chdev( chdev_ptr, node_name, (vfs_inode_t *)GET_PTR( external_inode_xp ), &chdev_inode_xp ); } // create NIC_TX chdev inodes for( channel = 0 ; channel < CONFIG_MAX_NIC_CHANNELS ; channel++ ) { chdev_ptr = (chdev_t *)GET_PTR( chdev_dir.nic_tx[channel] ); snprintf( node_name , 16 , "nic_tx_%d" , channel ); devfs_register_chdev( chdev_ptr, node_name, (vfs_inode_t *)GET_PTR( external_inode_xp ), &chdev_inode_xp ); } } // synchronize all clusters remote_barrier( barrier_xp , nb_clusters ); return 0; } // end devfs_init() //////////////////////////////////////////// error_t devfs_ctx_init( vfs_ctx_t * vfs_ctx, xptr_t root_inode_xp ) { vfs_ctx->type = FS_TYPE_DEVFS; vfs_ctx->attr = 0; // not READ_ONLY / not SYNC vfs_ctx->count = 0; // unused for DEVFS vfs_ctx->blksize = 0; // unused for DEVFS vfs_ctx->root_xp = root_inode_xp; vfs_ctx->extend = NULL; // unused for DEVFS spinlock_init( &vfs_ctx->lock ); bitmap_init( vfs_ctx->bitmap , CONFIG_VFS_MAX_INODES ); return 0; } //////////////////////////////////////////////////// error_t devfs_inode_create( vfs_inode_t * vfs_inode, chdev_t * chdev ) { kmem_req_t req; devfs_inode_t * devfs_inode; // allocate memory for FATFS inode extension req.type = KMEM_DEVFS_INODE; req.size = sizeof(devfs_inode_t); req.flags = AF_KERNEL | AF_ZERO; devfs_inode = (devfs_inode_t *)kmem_alloc( &req ); if( devfs_inode == NULL ) return ENOMEM; // link DEVFS inode to VFS inode vfs_inode->extend = devfs_inode; // initialise DEVFS inode devfs_inode->chdev = chdev; return 0; } /////////////////////////////////////////////////// void devfs_inode_destroy( vfs_inode_t * vfs_inode ) { kmem_req_t req; devfs_inode_t * devfs_inode; // get pointer on DEVFS inode devfs_inode = (devfs_inode_t *)vfs_inode->extend; req.type = KMEM_DEVFS_INODE; req.ptr = devfs_inode; kmem_free( &req ); vfs_inode->extend = NULL; } /* deprecated [AG] error_t devfs_open_file( vfs_file_t * file, void * extend ); { error_t err; register struct devfs_context_s *ctx; register struct devfs_file_s *info; chdev_t * chdev; vfs_inode_t * inode; dev_request_t rq; kmem_req_t req; inode = file->inode; info = file->fr_pv; ctx = (struct devfs_context_s *)&inode->i_ctx->ctx_devfs; if(!(inode->i_attr & VFS_DIR)) { dev = (struct device_s*)inode->i_pv; if(dev->type == DEV_INTERNAL) return EPERM; if(dev->type == DEV_BLK) VFS_SET(inode->i_attr,VFS_DEV_BLK); else VFS_SET(inode->i_attr,VFS_DEV_CHR); if(dev->op.dev.open != NULL) { rq.fremote = file; if((err=dev->op.dev.open(dev, &rq))) return err; } priv->dev = (void*)dev; return 0; } if(info == NULL) { req.type = KMEM_DEVFS_FILE; req.size = sizeof(*info); req.flags = AF_KERNEL; info = kmem_alloc(&req); } if(info == NULL) return ENOMEM; metafs_iter_init(&devfs_db.root, &info->iter); info->ctx = ctx; file->fr_pv = info; metafs_print(&devfs_db.root); return 0; } #define TMP_BUFF_SZ 512 //FIXME: //add a "while" loop for the case where the //buffer TMP_BUFF_SZ is smaller than //buffer->size ////////////////////////////// devfs_read( vfs_file_t * file, char * buffer ) { chdev_t * chdev; dev_request_t rq; uint32_t count; uint8_t buff[TMP_BUFF_SZ]; // get pointer on chdev chdev = (chdev_t *)file->extend; if( chdev->func == DEV_FUNC_TXT ) { } if( chdev->func == DEV_FUNC_IOC ) { } else { printk("\n[PANIC] in %s : illegal device functionnal type rq.dst = &buff[0]; rq.count = TMP_BUFF_SZ; rq.flags = 0; rq.file = file; if((count = dev->op.dev.read(dev, &rq)) < 0) return count; buffer->scpy_to_buff(buffer, &buff[0], count); return count; } //FIXME: To improve this an avoid the extra copy, //we could set along with the buffer(src and dest) //the functions to manipulate them, such as in //do_exec.c /////////////////////////////// devfs_write( vfs_file_t * file, char * buffer ) { register struct device_s *dev; uint8_t buff[TMP_BUFF_SZ]; dev_request_t rq; dev = (struct device_s*)file->f_private.dev; //FIXME avoid the extra copy ? buffer->scpy_from_buff(buffer, (void*)&buff[0], TMP_BUFF_SZ); rq.src = (void*)&buff[0]; rq.count = buffer->size; rq.flags = 0; rq.file = file; return dev->op.dev.write(dev, &rq); } VFS_LSEEK_FILE(devfs_lseek) { register struct device_s *dev; dev_request_t rq; dev = (struct device_s*)file->fr_inode->i_pv; if(dev->op.dev.lseek == NULL) return VFS_ENOTUSED; rq.fremote = file; return dev->op.dev.lseek(dev, &rq); } VFS_CLOSE_FILE(devfs_close) { register struct device_s *dev; dev_request_t rq; if(file->fr_inode->i_attr & VFS_DIR) return 0; dev = (struct device_s*)file->fr_inode->i_pv; if(dev->op.dev.close == NULL) return 0; rq.fremote = file; return dev->op.dev.close(dev, &rq); } VFS_RELEASE_FILE(devfs_release) { kmem_req_t req; if(file->fr_pv == NULL) return 0; req.type = KMEM_DEVFS_FILE; req.ptr = file->fr_pv; kmem_free(&req); file->fr_pv = NULL; return 0; } VFS_READ_DIR(devfs_readdir) { register struct devfs_file_s *info; register struct metafs_s *current; register struct device_s *current_dev; info = file->fr_pv; if(file->fr_pv == NULL) return ENOTDIR; if((current = metafs_lookup_next(&devfs_db.root, &info->iter)) == NULL) return EEODIR; current_dev = metafs_container(current, struct device_s, node); dirent->u_attr = (current_dev->type == DEV_BLK) ? VFS_DEV_BLK : VFS_DEV_CHR; strcpy((char*)dirent->u_name, metafs_get_name(current)); dirent->u_ino = (uint_t) current_dev->base_paddr; return 0; } VFS_MMAP_FILE(devfs_mmap) { register struct device_s *dev; dev_request_t rq; dev = (struct device_s*)file->f_private.dev; if(dev->op.dev.mmap == NULL) return ENODEV; rq.flags = 0; rq.file = file; rq.region = region; return dev->op.dev.mmap(dev, &rq); } VFS_MMAP_FILE(devfs_munmap) { register struct device_s *dev; dev_request_t rq; dev = (struct device_s*)file->f_private.dev; if(dev->op.dev.munmap == NULL) return ENODEV; rq.flags = 0; rq.file = file; rq.region = region; return dev->op.dev.munmap(dev, &rq); } */