/* * kernel_init.c - kernel parallel initialization * * Authors : Mohamed Lamine Karaoui (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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /////////////////////////////////////////////////////////////////////////////////////////// // All the following global variables are replicated in all clusters. // They are initialised by the kernel_init() function. // // WARNING : The section names have been defined to control the base addresses of the // boot_info structure and the idle thread descriptors, through the kernel.ld script: // - the boot_info structure is built by the bootloader, and used by kernel_init. // it must be the first object in the kdata segment. // - the array of idle threads descriptors must be placed on the first page boundary after // the boot_info structure in the kdata segment. /////////////////////////////////////////////////////////////////////////////////////////// // This variable defines the local boot_info structure __attribute__((section(".kinfo"))) boot_info_t boot_info; // This variable defines the "idle" threads descriptors array __attribute__((section(".kidle"))) char idle_threads[CONFIG_THREAD_DESC_SIZE * CONFIG_MAX_LOCAL_CORES] CONFIG_PPM_PAGE_ALIGNED; // This variable defines the local cluster manager __attribute__((section(".kdata"))) cluster_t cluster_manager CONFIG_CACHE_LINE_ALIGNED; // This variable defines the TXT0 kernel terminal (TX only) __attribute__((section(".kdata"))) chdev_t txt0_chdev CONFIG_CACHE_LINE_ALIGNED; // This variables define the kernel process0 descriptor __attribute__((section(".kdata"))) process_t process_zero CONFIG_CACHE_LINE_ALIGNED; // This variable defines extended pointers on the distributed chdevs __attribute__((section(".kdata"))) chdev_directory_t chdev_dir CONFIG_CACHE_LINE_ALIGNED; // This variable contains the input IRQ indexes for the IOPIC controller __attribute__((section(".kdata"))) iopic_input_t iopic_input CONFIG_CACHE_LINE_ALIGNED; // This variable contains the input IRQ indexes for the LAPIC controller __attribute__((section(".kdata"))) lapic_input_t lapic_input CONFIG_CACHE_LINE_ALIGNED; // This variable defines the local cluster identifier __attribute__((section(".kdata"))) cxy_t local_cxy CONFIG_CACHE_LINE_ALIGNED; // This variable is used for CP0 cores synchronisation in kernel_init() __attribute__((section(".kdata"))) remote_barrier_t global_barrier CONFIG_CACHE_LINE_ALIGNED; // This variable is used for local cores synchronisation in kernel_init() __attribute__((section(".kdata"))) barrier_t local_barrier CONFIG_CACHE_LINE_ALIGNED; // This variable defines the array of supported File System contexts __attribute__((section(".kdata"))) vfs_ctx_t fs_context[FS_TYPES_NR] CONFIG_CACHE_LINE_ALIGNED; // kernel_init is the entry point defined in hal/tsar_mips32/kernel.ld // It will be used by the bootloader. extern void kernel_init( boot_info_t * info ); // these debug variables are used to analyse the sys_read() syscall timing #if DEBUG_SYS_READ uint32_t enter_sys_read; uint32_t exit_sys_read; uint32_t enter_devfs_read; uint32_t exit_devfs_read; uint32_t enter_txt_read; uint32_t exit_txt_read; uint32_t enter_chdev_cmd_read; uint32_t exit_chdev_cmd_read; uint32_t enter_chdev_server_read; uint32_t exit_chdev_server_read; uint32_t enter_tty_cmd_read; uint32_t exit_tty_cmd_read; uint32_t enter_tty_isr_read; uint32_t exit_tty_isr_read; #endif // these debug variables are used to analyse the sys_write() syscall timing #if DEBUG_SYS_WRITE uint32_t enter_sys_write; uint32_t exit_sys_write; uint32_t enter_devfs_write; uint32_t exit_devfs_write; uint32_t enter_txt_write; uint32_t exit_txt_write; uint32_t enter_chdev_cmd_write; uint32_t exit_chdev_cmd_write; uint32_t enter_chdev_server_write; uint32_t exit_chdev_server_write; uint32_t enter_tty_cmd_write; uint32_t exit_tty_cmd_write; uint32_t enter_tty_isr_write; uint32_t exit_tty_isr_write; #endif /////////////////////////////////////////////////////////////////////////////////////////// // This function displays the ALMOS_MKH banner. /////////////////////////////////////////////////////////////////////////////////////////// static void print_banner( uint32_t nclusters , uint32_t ncores ) { printk("\n" " _ __ __ _____ ______ __ __ _ __ _ _ \n" " /\\ | | | \\ / | / ___ \\ / _____| | \\ / | | | / / | | | | \n" " / \\ | | | \\/ | | / \\ | | / | \\/ | | |/ / | | | | \n" " / /\\ \\ | | | |\\ /| | | | | | | |_____ ___ | |\\ /| | | / | |___| | \n" " / /__\\ \\ | | | | \\/ | | | | | | \\_____ \\ |___| | | \\/ | | | \\ | ___ | \n" " / ______ \\ | | | | | | | | | | | | | | | | | |\\ \\ | | | | \n" " / / \\ \\ | |____ | | | | | \\___/ | _____/ | | | | | | | \\ \\ | | | | \n" " /_/ \\_\\ |______| |_| |_| \\_____/ |______/ |_| |_| |_| \\_\\ |_| |_| \n" "\n\n\t\t Advanced Locality Management Operating System / Multi Kernel Hybrid\n" "\n\n\t\t %s / %d cluster(s) / %d core(s) per cluster\n\n", CONFIG_ALMOS_VERSION , nclusters , ncores ); } /////////////////////////////////////////////////////////////////////////////////////////// // This function initializes the TXT0 chdev descriptor, that is the "kernel terminal", // shared by all kernel instances for debug messages. // It is a global variable (replicated in all clusters), because this terminal is used // before the kmem allocator initialisation, but only the instance in cluster containing // the calling core is registered in the "chdev_dir" directory. // As this TXT0 chdev supports only the TXT_SYNC_WRITE command, we don't create // a server thread, we don't allocate a WTI, and we don't initialize the waiting queue. /////////////////////////////////////////////////////////////////////////////////////////// // @ info : pointer on the local boot-info structure. /////////////////////////////////////////////////////////////////////////////////////////// static void txt0_device_init( boot_info_t * info ) { boot_device_t * dev_tbl; // pointer on array of devices in boot_info uint32_t dev_nr; // actual number of devices in this cluster xptr_t base; // remote pointer on segment base uint32_t func; // device functional index uint32_t impl; // device implementation index uint32_t i; // device index in dev_tbl uint32_t x; // X cluster coordinate uint32_t y; // Y cluster coordinate uint32_t channels; // number of channels // get number of peripherals and base of devices array from boot_info dev_nr = info->ext_dev_nr; dev_tbl = info->ext_dev; // loop on external peripherals to find TXT device for( i = 0 ; i < dev_nr ; i++ ) { base = dev_tbl[i].base; func = FUNC_FROM_TYPE( dev_tbl[i].type ); impl = IMPL_FROM_TYPE( dev_tbl[i].type ); channels = dev_tbl[i].channels; if (func == DEV_FUNC_TXT ) { assert( (channels > 0) , "number of TXT channels cannot be 0\n"); // initializes TXT_TX[0] chdev txt0_chdev.func = func; txt0_chdev.impl = impl; txt0_chdev.channel = 0; txt0_chdev.base = base; txt0_chdev.is_rx = false; // initializes lock remote_spinlock_init( XPTR( local_cxy , &txt0_chdev.wait_lock ) ); // TXT specific initialisation: // no server thread & no IRQ routing for channel 0 dev_txt_init( &txt0_chdev ); // register the TXT0 in all chdev_dir[x][y] structures for( x = 0 ; x < info->x_size ; x++ ) { for( y = 0 ; y < info->y_size ; y++ ) { cxy_t cxy = (x<y_width) + y; hal_remote_swd( XPTR( cxy , &chdev_dir.txt_tx[0] ) , XPTR( local_cxy , &txt0_chdev ) ); } } } } // end loop on devices } // end txt0_device_init() /////////////////////////////////////////////////////////////////////////////////////////// // This function allocates memory and initializes the chdev descriptors for the internal // peripherals contained in the local cluster, other than the LAPIC, as specified by // the boot_info, including the linking with the driver for the specified implementation. // The relevant entries in all copies of the devices directory are initialised. /////////////////////////////////////////////////////////////////////////////////////////// // @ info : pointer on the local boot-info structure. /////////////////////////////////////////////////////////////////////////////////////////// static void internal_devices_init( boot_info_t * info ) { boot_device_t * dev_tbl; // pointer on array of internaldevices in boot_info uint32_t dev_nr; // actual number of devices in this cluster xptr_t base; // remote pointer on segment base uint32_t func; // device functionnal index uint32_t impl; // device implementation index uint32_t i; // device index in dev_tbl uint32_t x; // X cluster coordinate uint32_t y; // Y cluster coordinate uint32_t channels; // number of channels uint32_t channel; // channel index chdev_t * chdev_ptr; // local pointer on created chdev // get number of internal peripherals and base from boot_info dev_nr = info->int_dev_nr; dev_tbl = info->int_dev; // loop on internal peripherals for( i = 0 ; i < dev_nr ; i++ ) { base = dev_tbl[i].base; channels = dev_tbl[i].channels; func = FUNC_FROM_TYPE( dev_tbl[i].type ); impl = IMPL_FROM_TYPE( dev_tbl[i].type ); ////////////////////////// if( func == DEV_FUNC_MMC ) { assert( (channels == 1) , "MMC device must be single channel\n" ); // create chdev in local cluster chdev_ptr = chdev_create( func, impl, 0, // channel false, // direction base ); assert( (chdev_ptr != NULL) , "cannot allocate memory for MMC chdev\n" ); // make MMC specific initialisation dev_mmc_init( chdev_ptr ); // set the MMC field in all chdev_dir[x][y] structures for( x = 0 ; x < info->x_size ; x++ ) { for( y = 0 ; y < info->y_size ; y++ ) { cxy_t cxy = (x<y_width) + y; hal_remote_swd( XPTR( cxy , &chdev_dir.mmc[local_cxy] ), XPTR( local_cxy , chdev_ptr ) ); } } #if( DEBUG_KERNEL_INIT & 0x1 ) if( hal_time_stamp() > DEBUG_KERNEL_INIT ) printk("\n[DBG] %s : created MMC in cluster %x / chdev = %x\n", __FUNCTION__ , local_cxy , chdev_ptr ); #endif } /////////////////////////////// else if( func == DEV_FUNC_DMA ) { // create one chdev per channel in local cluster for( channel = 0 ; channel < channels ; channel++ ) { // create chdev[channel] in local cluster chdev_ptr = chdev_create( func, impl, channel, false, // direction base ); assert( (chdev_ptr != NULL) , "cannot allocate memory for DMA chdev" ); // make DMA specific initialisation dev_dma_init( chdev_ptr ); // initialize only the DMA[channel] field in the local chdev_dir[x][y] // structure because the DMA device is not remotely accessible. chdev_dir.dma[channel] = XPTR( local_cxy , chdev_ptr ); #if( DEBUG_KERNEL_INIT & 0x1 ) if( hal_time_stamp() > DEBUG_KERNEL_INIT ) printk("\n[DBG] %s : created DMA[%d] in cluster %x / chdev = %x\n", __FUNCTION__ , channel , local_cxy , chdev_ptr ); #endif } } } } // end internal_devices_init() /////////////////////////////////////////////////////////////////////////////////////////// // This function allocates memory and initializes the chdev descriptors for the // external (shared) peripherals other than the IOPIC, as specified by the boot_info. // This includes the dynamic linking with the driver for the specified implementation. // These chdev descriptors are distributed on all clusters, using a modulo on a global // index, identically computed in all clusters. // This function is executed in all clusters by the CP0 core, that computes a global index // for all external chdevs. Each CP0 core creates only the chdevs that must be placed in // the local cluster, because the global index matches the local index. // The relevant entries in all copies of the devices directory are initialised. /////////////////////////////////////////////////////////////////////////////////////////// // @ info : pointer on the local boot-info structure. /////////////////////////////////////////////////////////////////////////////////////////// static void external_devices_init( boot_info_t * info ) { boot_device_t * dev_tbl; // pointer on array of external devices in boot_info uint32_t dev_nr; // actual number of external devices xptr_t base; // remote pointer on segment base uint32_t func; // device functionnal index uint32_t impl; // device implementation index uint32_t i; // device index in dev_tbl uint32_t x; // X cluster coordinate uint32_t y; // Y cluster coordinate uint32_t channels; // number of channels uint32_t channel; // channel index uint32_t directions; // number of directions (1 or 2) uint32_t rx; // direction index (0 or 1) chdev_t * chdev; // local pointer on one channel_device descriptor uint32_t ext_chdev_gid; // global index of external chdev // get number of peripherals and base of devices array from boot_info dev_nr = info->ext_dev_nr; dev_tbl = info->ext_dev; // initializes global index (PIC is already placed in cluster 0 ext_chdev_gid = 1; // loop on external peripherals for( i = 0 ; i < dev_nr ; i++ ) { base = dev_tbl[i].base; channels = dev_tbl[i].channels; func = FUNC_FROM_TYPE( dev_tbl[i].type ); impl = IMPL_FROM_TYPE( dev_tbl[i].type ); // There is one chdev per direction for NIC and for TXT if((func == DEV_FUNC_NIC) || (func == DEV_FUNC_TXT)) directions = 2; else directions = 1; // do nothing for ROM, that does not require a device descriptor. if( func == DEV_FUNC_ROM ) continue; // do nothing for PIC, that is already initialized if( func == DEV_FUNC_PIC ) continue; // check PIC device initialized assert( (chdev_dir.pic != XPTR_NULL ) , "PIC device must be initialized before other devices\n" ); // check external device functionnal type assert( ( (func == DEV_FUNC_IOB) || (func == DEV_FUNC_IOC) || (func == DEV_FUNC_TXT) || (func == DEV_FUNC_NIC) || (func == DEV_FUNC_FBF) ) , "undefined external peripheral type\n" ); // loops on channels for( channel = 0 ; channel < channels ; channel++ ) { // loop on directions for( rx = 0 ; rx < directions ; rx++ ) { // skip TXT_TX[0] chdev that has already been created & registered if( (func == DEV_FUNC_TXT) && (channel == 0) && (rx == 0) ) continue; // compute target cluster for chdev[func,channel,direction] uint32_t offset = ext_chdev_gid % ( info->x_size * info->y_size ); uint32_t cx = offset / info->y_size; uint32_t cy = offset % info->y_size; uint32_t target_cxy = (cx<y_width) + cy; // allocate and initialize a local chdev // when local cluster matches target cluster if( target_cxy == local_cxy ) { chdev = chdev_create( func, impl, channel, rx, // direction base ); assert( (chdev != NULL), "cannot allocate external device" ); // make device type specific initialisation if ( func == DEV_FUNC_IOB ) dev_iob_init( chdev ); else if( func == DEV_FUNC_IOC ) dev_ioc_init( chdev ); else if( func == DEV_FUNC_TXT ) dev_txt_init( chdev ); else if( func == DEV_FUNC_NIC ) dev_nic_init( chdev ); else if( func == DEV_FUNC_FBF ) dev_fbf_init( chdev ); // all external (shared) devices are remotely accessible // initialize the replicated chdev_dir[x][y] structures // defining the extended pointers on chdev descriptors xptr_t * entry; if(func==DEV_FUNC_IOB ) entry = &chdev_dir.iob; if(func==DEV_FUNC_IOC ) entry = &chdev_dir.ioc[channel]; if(func==DEV_FUNC_FBF ) entry = &chdev_dir.fbf[channel]; if((func==DEV_FUNC_TXT) && (rx==0)) entry = &chdev_dir.txt_tx[channel]; if((func==DEV_FUNC_TXT) && (rx==1)) entry = &chdev_dir.txt_rx[channel]; if((func==DEV_FUNC_NIC) && (rx==0)) entry = &chdev_dir.nic_tx[channel]; if((func==DEV_FUNC_NIC) && (rx==1)) entry = &chdev_dir.nic_rx[channel]; for( x = 0 ; x < info->x_size ; x++ ) { for( y = 0 ; y < info->y_size ; y++ ) { cxy_t cxy = (x<y_width) + y; hal_remote_swd( XPTR( cxy , entry ), XPTR( local_cxy , chdev ) ); } } #if( DEBUG_KERNEL_INIT & 0x1 ) if( hal_time_stamp() > DEBUG_KERNEL_INIT ) printk("\n[DBG] %s : create chdev %s / channel = %d / rx = %d / cluster %x / chdev = %x\n", __FUNCTION__ , chdev_func_str( func ), channel , rx , local_cxy , chdev ); #endif } // end if match // increment chdev global index (matching or not) ext_chdev_gid++; } // end loop on directions } // end loop on channels } // end loop on devices } // end external_devices_init() /////////////////////////////////////////////////////////////////////////////////////////// // This function is called by CP0 in cluster 0 to allocate memory and initialize the PIC // device, namely the informations attached to the external IOPIC controller, that // must be replicated in all clusters (struct iopic_input). // This initialisation must be done before other devices initialisation because the IRQ // routing infrastructure is required for both internal and external devices init. /////////////////////////////////////////////////////////////////////////////////////////// // @ info : pointer on the local boot-info structure. /////////////////////////////////////////////////////////////////////////////////////////// static void iopic_init( boot_info_t * info ) { boot_device_t * dev_tbl; // pointer on boot_info external devices array uint32_t dev_nr; // actual number of external devices xptr_t base; // remote pointer on segment base uint32_t func; // device functionnal index uint32_t impl; // device implementation index uint32_t i; // device index in dev_tbl uint32_t x; // cluster X coordinate uint32_t y; // cluster Y coordinate bool_t found; // IOPIC found chdev_t * chdev; // pointer on PIC chdev descriptor // get number of external peripherals and base of array from boot_info dev_nr = info->ext_dev_nr; dev_tbl = info->ext_dev; // loop on external peripherals to get the IOPIC for( i = 0 , found = false ; i < dev_nr ; i++ ) { func = FUNC_FROM_TYPE( dev_tbl[i].type ); if( func == DEV_FUNC_PIC ) { base = dev_tbl[i].base; impl = IMPL_FROM_TYPE( dev_tbl[i].type ); found = true; break; } } assert( found , "PIC device not found\n" ); // allocate and initialize the PIC chdev in cluster 0 chdev = chdev_create( DEV_FUNC_PIC, impl, 0, // channel 0, // direction, base ); assert( (chdev != NULL), "no memory for PIC chdev\n" ); // make PIC device type specific initialisation dev_pic_init( chdev ); // register, in all clusters, the extended pointer // on PIC chdev in "chdev_dir" array xptr_t * entry = &chdev_dir.pic; for( x = 0 ; x < info->x_size ; x++ ) { for( y = 0 ; y < info->y_size ; y++ ) { cxy_t cxy = (x<y_width) + y; hal_remote_swd( XPTR( cxy , entry ) , XPTR( local_cxy , chdev ) ); } } // initialize, in all clusters, the "iopic_input" structure // defining how external IRQs are connected to IOPIC // register default value for unused inputs for( x = 0 ; x < info->x_size ; x++ ) { for( y = 0 ; y < info->y_size ; y++ ) { cxy_t cxy = (x<y_width) + y; hal_remote_memset( XPTR( cxy , &iopic_input ) , 0xFF , sizeof(iopic_input_t) ); } } // register input IRQ index for valid inputs uint32_t id; // input IRQ index uint8_t valid; // input IRQ is connected uint32_t type; // source device type uint8_t channel; // source device channel uint8_t is_rx; // source device direction uint32_t * ptr; // local pointer on one field in iopic_input stucture for( id = 0 ; id < CONFIG_MAX_EXTERNAL_IRQS ; id++ ) { valid = dev_tbl[i].irq[id].valid; type = dev_tbl[i].irq[id].dev_type; channel = dev_tbl[i].irq[id].channel; is_rx = dev_tbl[i].irq[id].is_rx; func = FUNC_FROM_TYPE( type ); // get pointer on relevant field in iopic_input if( valid ) { if ( func == DEV_FUNC_IOC ) ptr = &iopic_input.ioc[channel]; else if((func == DEV_FUNC_TXT) && (is_rx == 0)) ptr = &iopic_input.txt_tx[channel]; else if((func == DEV_FUNC_TXT) && (is_rx != 0)) ptr = &iopic_input.txt_rx[channel]; else if((func == DEV_FUNC_NIC) && (is_rx == 0)) ptr = &iopic_input.nic_tx[channel]; else if((func == DEV_FUNC_NIC) && (is_rx != 0)) ptr = &iopic_input.nic_rx[channel]; else if( func == DEV_FUNC_IOB ) ptr = &iopic_input.iob; else assert( false , "illegal source device for IOPIC input" ); // set one entry in all "iopic_input" structures for( x = 0 ; x < info->x_size ; x++ ) { for( y = 0 ; y < info->y_size ; y++ ) { cxy_t cxy = (x<y_width) + y; hal_remote_swd( XPTR( cxy , ptr ) , id ); } } } } #if( DEBUG_KERNEL_INIT & 0x1 ) if( hal_time_stamp() > DEBUG_KERNEL_INIT ) { printk("\n[DBG] %s created PIC chdev in cluster %x at cycle %d\n", __FUNCTION__ , local_cxy , (uint32_t)hal_time_stamp() ); dev_pic_inputs_display(); } #endif } // end iopic_init() /////////////////////////////////////////////////////////////////////////////////////////// // This function is called by all CP0s in all cluster to complete the PIC device // initialisation, namely the informations attached to the LAPIC controller. // This initialisation must be done after the IOPIC initialisation, but before other // devices initialisation because the IRQ routing infrastructure is required for both // internal and external devices initialisation. /////////////////////////////////////////////////////////////////////////////////////////// // @ info : pointer on the local boot-info structure. /////////////////////////////////////////////////////////////////////////////////////////// static void lapic_init( boot_info_t * info ) { boot_device_t * dev_tbl; // pointer on boot_info internal devices array uint32_t dev_nr; // number of internal devices uint32_t i; // device index in dev_tbl xptr_t base; // remote pointer on segment base uint32_t func; // device functionnal type in boot_info bool_t found; // LAPIC found // get number of internal peripherals and base dev_nr = info->int_dev_nr; dev_tbl = info->int_dev; // loop on internal peripherals to get the lapic device for( i = 0 , found = false ; i < dev_nr ; i++ ) { func = FUNC_FROM_TYPE( dev_tbl[i].type ); if( func == DEV_FUNC_ICU ) { base = dev_tbl[i].base; found = true; break; } } // if the LAPIC controller is not defined in the boot_info, // we simply don't initialize the PIC extensions in the kernel, // making the assumption that the LAPIC related informations // are hidden in the hardware specific PIC driver. if( found ) { // initialise the PIC extensions for // the core descriptor and core manager extensions dev_pic_extend_init( (uint32_t *)GET_PTR( base ) ); // initialize the "lapic_input" structure // defining how internal IRQs are connected to LAPIC uint32_t id; uint8_t valid; uint8_t channel; uint32_t func; for( id = 0 ; id < CONFIG_MAX_INTERNAL_IRQS ; id++ ) { valid = dev_tbl[i].irq[id].valid; func = FUNC_FROM_TYPE( dev_tbl[i].irq[id].dev_type ); channel = dev_tbl[i].irq[id].channel; if( valid ) // only valid local IRQs are registered { if ( func == DEV_FUNC_MMC ) lapic_input.mmc = id; else if( func == DEV_FUNC_DMA ) lapic_input.dma[channel] = id; else assert( false , "illegal source device for LAPIC input" ); } } } } // end lapic_init() /////////////////////////////////////////////////////////////////////////////////////////// // This static function returns the identifiers of the calling core. /////////////////////////////////////////////////////////////////////////////////////////// // @ info : pointer on boot_info structure. // @ lid : [out] core local index in cluster. // @ cxy : [out] cluster identifier. // @ lid : [out] core global identifier (hardware). // @ return 0 if success / return EINVAL if not found. /////////////////////////////////////////////////////////////////////////////////////////// static error_t get_core_identifiers( boot_info_t * info, lid_t * lid, cxy_t * cxy, gid_t * gid ) { uint32_t i; gid_t global_id; // get global identifier from hardware register global_id = hal_get_gid(); // makes an associative search in boot_info to get (cxy,lid) from global_id for( i = 0 ; i < info->cores_nr ; i++ ) { if( global_id == info->core[i].gid ) { *lid = info->core[i].lid; *cxy = info->core[i].cxy; *gid = global_id; return 0; } } return EINVAL; } /////////////////////////////////////////////////////////////////////////////////////////// // This function is the entry point for the kernel initialisation. // It is executed by all cores in all clusters, but only core[0], called CP0, // initializes the shared resources such as the cluster manager, or the local peripherals. // To comply with the multi-kernels paradigm, it accesses only local cluster memory, using // only information contained in the local boot_info_t structure, set by the bootloader. // Only CP0 in cluster 0 print the log messages. /////////////////////////////////////////////////////////////////////////////////////////// // @ info : pointer on the local boot-info structure. /////////////////////////////////////////////////////////////////////////////////////////// void kernel_init( boot_info_t * info ) { lid_t core_lid = -1; // running core local index cxy_t core_cxy = -1; // running core cluster identifier gid_t core_gid; // running core hardware identifier cluster_t * cluster; // pointer on local cluster manager core_t * core; // pointer on running core descriptor thread_t * thread; // pointer on idle thread descriptor xptr_t vfs_root_inode_xp; // extended pointer on VFS root inode xptr_t devfs_dev_inode_xp; // extended pointer on DEVFS dev inode xptr_t devfs_external_inode_xp; // extended pointer on DEVFS external inode xptr_t devfs_internal_inode_xp; // extended pointer on DEVFS internal inode error_t error; reg_t status; // running core status register ///////////////////////////////////////////////////////////////////////////////// // STEP 0 : Each core get its core identifier from boot_info, and makes // a partial initialisation of its private idle thread descriptor. // CP0 initializes the "local_cxy" global variable. // CP0 in cluster IO initializes the TXT0 chdev to print log messages. ///////////////////////////////////////////////////////////////////////////////// error = get_core_identifiers( info, &core_lid, &core_cxy, &core_gid ); // CP0 initializes cluster identifier if( core_lid == 0 ) local_cxy = info->cxy; // each core gets a pointer on its private idle thread descriptor thread = (thread_t *)( idle_threads + (core_lid * CONFIG_THREAD_DESC_SIZE) ); // each core registers this thread pointer in hardware register hal_set_current_thread( thread ); // each core register core descriptor pointer in idle thread descriptor thread->core = &LOCAL_CLUSTER->core_tbl[core_lid]; // each core initializes the idle thread lists of locks list_root_init( &thread->locks_root ); xlist_root_init( XPTR( local_cxy , &thread->xlocks_root ) ); thread->local_locks = 0; thread->remote_locks = 0; // CP0 in cluster 0 initialises TXT0 chdev descriptor if( (core_lid == 0) && (core_cxy == 0) ) txt0_device_init( info ); ///////////////////////////////////////////////////////////////////////////////// if( core_lid == 0 ) remote_barrier( XPTR( 0 , &global_barrier ), (info->x_size * info->y_size) ); barrier_wait( &local_barrier , info->cores_nr ); ///////////////////////////////////////////////////////////////////////////////// #if DEBUG_KERNEL_INIT if( (core_lid == 0) & (local_cxy == 0) ) printk("\n[DBG] %s : exit barrier 0 : TXT0 initialized / cycle %d\n", __FUNCTION__, (uint32_t)hal_get_cycles() ); #endif ///////////////////////////////////////////////////////////////////////////// // STEP 1 : all cores check core identifier. // CP0 initializes the local cluster manager. // This includes the memory allocators. ///////////////////////////////////////////////////////////////////////////// // all cores check identifiers if( error ) { assert( false , "illegal core identifiers gid = %x / cxy = %x / lid = %d", core_lid , core_cxy , core_lid ); } // CP0 initializes cluster manager if( core_lid == 0 ) { error = cluster_init( info ); if( error ) { assert( false , "cannot initialise cluster %x", local_cxy ); } } ///////////////////////////////////////////////////////////////////////////////// if( core_lid == 0 ) remote_barrier( XPTR( 0 , &global_barrier ), (info->x_size * info->y_size) ); barrier_wait( &local_barrier , info->cores_nr ); ///////////////////////////////////////////////////////////////////////////////// #if DEBUG_KERNEL_INIT if( (core_lid == 0) & (local_cxy == 0) ) printk("\n[DBG] %s : exit barrier 1 : clusters initialised / cycle %d\n", __FUNCTION__, (uint32_t)hal_get_cycles() ); #endif ///////////////////////////////////////////////////////////////////////////////// // STEP 2 : CP0 initializes the process_zero descriptor. // CP0 in cluster 0 initializes the IOPIC device. ///////////////////////////////////////////////////////////////////////////////// // all cores get pointer on local cluster manager & core descriptor cluster = &cluster_manager; core = &cluster->core_tbl[core_lid]; // all CP0s initialize the process_zero descriptor if( core_lid == 0 ) process_zero_create( &process_zero ); // CP0 in cluster 0 initializes the PIC chdev, if( (core_lid == 0) && (local_cxy == 0) ) iopic_init( info ); //////////////////////////////////////////////////////////////////////////////// if( core_lid == 0 ) remote_barrier( XPTR( 0 , &global_barrier ), (info->x_size * info->y_size) ); barrier_wait( &local_barrier , info->cores_nr ); //////////////////////////////////////////////////////////////////////////////// #if DEBUG_KERNEL_INIT if( (core_lid == 0) & (local_cxy == 0) ) printk("\n[DBG] %s : exit barrier 2 : PIC initialised / cycle %d\n", __FUNCTION__, (uint32_t)hal_get_cycles() ); #endif //////////////////////////////////////////////////////////////////////////////// // STEP 3 : CP0 initializes the distibuted LAPIC descriptor. // CP0 initializes the internal chdev descriptors // CP0 initialize the local external chdev descriptors //////////////////////////////////////////////////////////////////////////////// // all CP0s initialize their local LAPIC extension, if( core_lid == 0 ) lapic_init( info ); // CP0 scan the internal (private) peripherals, // and allocates memory for the corresponding chdev descriptors. if( core_lid == 0 ) internal_devices_init( info ); // All CP0s contribute to initialise external peripheral chdev descriptors. // Each CP0[cxy] scan the set of external (shared) peripherals (but the TXT0), // and allocates memory for the chdev descriptors that must be placed // on the (cxy) cluster according to the global index value. if( core_lid == 0 ) external_devices_init( info ); ///////////////////////////////////////////////////////////////////////////////// if( core_lid == 0 ) remote_barrier( XPTR( 0 , &global_barrier ), (info->x_size * info->y_size) ); barrier_wait( &local_barrier , info->cores_nr ); ///////////////////////////////////////////////////////////////////////////////// #if DEBUG_KERNEL_INIT if( (core_lid == 0) & (local_cxy == 0) ) printk("\n[DBG] %s : exit barrier 3 : all chdev initialised / cycle %d\n", __FUNCTION__, (uint32_t)hal_get_cycles() ); #endif #if( DEBUG_KERNEL_INIT & 1 ) if( (core_lid == 0) & (local_cxy == 0) ) chdev_dir_display(); #endif ///////////////////////////////////////////////////////////////////////////////// // STEP 4 : All cores enable IPI (Inter Procesor Interrupt), // Alh cores initialize IDLE thread. // Only CP0 in cluster 0 creates the VFS root inode. // It access the boot device to initialize the file system context. ///////////////////////////////////////////////////////////////////////////////// // All cores enable the shared IPI channel dev_pic_enable_ipi(); hal_enable_irq( &status ); // all cores initialize the idle thread descriptor thread_idle_init( thread, THREAD_IDLE, &thread_idle_func, NULL, core_lid ); // all cores unblock idle thread, and register it in scheduler thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_GLOBAL ); core->scheduler.idle = thread; #if( DEBUG_KERNEL_INIT & 1 ) sched_display( core_lid ); #endif // CPO in cluster 0 creates the VFS root if( (core_lid == 0) && (local_cxy == 0 ) ) { vfs_root_inode_xp = XPTR_NULL; // File System must be FATFS in this implementation, // but other File System can be introduced here if( CONFIG_VFS_ROOT_IS_FATFS ) { // 1. allocate memory for FATFS context in cluster 0 fatfs_ctx_t * fatfs_ctx = fatfs_ctx_alloc(); assert( (fatfs_ctx != NULL) , "cannot create FATFS context in cluster 0\n" ); // 2. access boot device to initialize FATFS context fatfs_ctx_init( fatfs_ctx ); // 3. get various informations from FATFS context uint32_t root_dir_cluster = fatfs_ctx->root_dir_cluster; uint32_t cluster_size = fatfs_ctx->bytes_per_sector * fatfs_ctx->sectors_per_cluster; uint32_t total_clusters = fatfs_ctx->fat_sectors_count << 7; // 4. create VFS root inode in cluster 0 error = vfs_inode_create( XPTR_NULL, // dentry_xp FS_TYPE_FATFS, // fs_type INODE_TYPE_DIR, // inode_type (void *)(intptr_t)root_dir_cluster, // extend 0, // attr 0, // rights 0, // uid 0, // gid &vfs_root_inode_xp ); // return assert( (error == 0) , "cannot create VFS root inode\n" ); // 5. initialize VFS context for FAT in cluster 0 vfs_ctx_init( FS_TYPE_FATFS, // file system type 0, // attributes total_clusters, cluster_size, vfs_root_inode_xp, // VFS root fatfs_ctx ); // extend // 6. check initialisation vfs_ctx_t * vfs_ctx = &fs_context[FS_TYPE_FATFS]; assert( (((fatfs_ctx_t *)vfs_ctx->extend)->sectors_per_cluster == 8), "illegal value for FATFS context in cluster %x\n", local_cxy ); } else { assert( false , "root FS must be FATFS" ); } // register VFS root inode in process_zero descriptor of cluster 0 process_zero.vfs_root_xp = vfs_root_inode_xp; process_zero.vfs_cwd_xp = vfs_root_inode_xp; } ///////////////////////////////////////////////////////////////////////////////// if( core_lid == 0 ) remote_barrier( XPTR( 0 , &global_barrier ), (info->x_size * info->y_size) ); barrier_wait( &local_barrier , info->cores_nr ); ///////////////////////////////////////////////////////////////////////////////// #if DEBUG_KERNEL_INIT if( (core_lid == 0) & (local_cxy == 0) ) printk("\n[DBG] %s : exit barrier 4 : VFS_root = %l in cluster 0 / cycle %d\n", __FUNCTION__, vfs_root_inode_xp , (uint32_t)hal_get_cycles()); #endif ///////////////////////////////////////////////////////////////////////////////// // STEP 5 : Other CP0s allocate memory for the selected FS context, // and initialise both the local FS context and the local VFS context // from values stored in cluster 0. // They get the VFS root inode extended pointer from cluster 0. ///////////////////////////////////////////////////////////////////////////////// if( (core_lid == 0) && (local_cxy != 0) ) { // File System must be FATFS in this implementation, // but other File System can be introduced here if( CONFIG_VFS_ROOT_IS_FATFS ) { // 1. allocate memory for local FATFS context fatfs_ctx_t * local_fatfs_ctx = fatfs_ctx_alloc(); assert( (local_fatfs_ctx != NULL) , "cannot create FATFS context in cluster %x\n", local_cxy ); // 2. get local pointer on VFS context for FATFS vfs_ctx_t * vfs_ctx = &fs_context[FS_TYPE_FATFS]; // 3. get local pointer on FATFS context in cluster 0 fatfs_ctx_t * remote_fatfs_ctx = hal_remote_lpt( XPTR( 0 , &vfs_ctx->extend ) ); // 4. copy FATFS context from cluster 0 to local cluster hal_remote_memcpy( XPTR( local_cxy , local_fatfs_ctx ), XPTR( 0 , remote_fatfs_ctx ), sizeof(fatfs_ctx_t) ); // 5. copy VFS context from cluster 0 to local cluster hal_remote_memcpy( XPTR( local_cxy , vfs_ctx ), XPTR( 0 , vfs_ctx ), sizeof(vfs_ctx_t) ); // 6. update extend field in local copy of VFS context vfs_ctx->extend = local_fatfs_ctx; // 7. check initialisation assert( (((fatfs_ctx_t *)vfs_ctx->extend)->sectors_per_cluster == 8), "illegal value for FATFS context in cluster %x\n", local_cxy ); } // get extended pointer on VFS root inode from cluster 0 vfs_root_inode_xp = hal_remote_lwd( XPTR( 0 , &process_zero.vfs_root_xp ) ); // update local process_zero descriptor process_zero.vfs_root_xp = vfs_root_inode_xp; process_zero.vfs_cwd_xp = vfs_root_inode_xp; } ///////////////////////////////////////////////////////////////////////////////// if( core_lid == 0 ) remote_barrier( XPTR( 0 , &global_barrier ), (info->x_size * info->y_size) ); barrier_wait( &local_barrier , info->cores_nr ); ///////////////////////////////////////////////////////////////////////////////// #if DEBUG_KERNEL_INIT if( (core_lid == 0) & (local_cxy == 0) ) printk("\n[DBG] %s : exit barrier 5 : VFS_root = %l in cluster 0 / cycle %d\n", __FUNCTION__, vfs_root_inode_xp , (uint32_t)hal_get_cycles()); #endif ///////////////////////////////////////////////////////////////////////////////// // STEP 6 : CP0 in cluster IO makes the global DEVFS tree initialisation: // It creates the DEVFS directory "dev", and the DEVFS "external" // directory in cluster IO and mount these inodes into VFS. ///////////////////////////////////////////////////////////////////////////////// if( (core_lid == 0) && (local_cxy == 0) ) { // create "dev" and "external" directories. devfs_global_init( process_zero.vfs_root_xp, &devfs_dev_inode_xp, &devfs_external_inode_xp ); // creates the DEVFS context in cluster IO devfs_ctx_t * devfs_ctx = devfs_ctx_alloc(); assert( (devfs_ctx != NULL) , "cannot create DEVFS context in cluster IO\n"); // register DEVFS root and external directories devfs_ctx_init( devfs_ctx, devfs_dev_inode_xp, devfs_external_inode_xp ); } ///////////////////////////////////////////////////////////////////////////////// if( core_lid == 0 ) remote_barrier( XPTR( 0 , &global_barrier ), (info->x_size * info->y_size) ); barrier_wait( &local_barrier , info->cores_nr ); ///////////////////////////////////////////////////////////////////////////////// #if DEBUG_KERNEL_INIT if( (core_lid == 0) & (local_cxy == 0) ) printk("\n[DBG] %s : exit barrier 6 : dev_root = %l in cluster 0 / cycle %d\n", __FUNCTION__, devfs_dev_inode_xp , (uint32_t)hal_get_cycles() ); #endif ///////////////////////////////////////////////////////////////////////////////// // STEP 7 : All CP0s complete in parallel the DEVFS tree initialization. // Each CP0 get the "dev" and "external" extended pointers from // values stored in cluster IO. // Then each CP0 in cluster(i) creates the DEVFS "internal directory, // and creates the pseudo-files for all chdevs in cluster (i). ///////////////////////////////////////////////////////////////////////////////// if( core_lid == 0 ) { // get extended pointer on "extend" field of VFS context for DEVFS in cluster 0 xptr_t extend_xp = XPTR( 0 , &fs_context[FS_TYPE_DEVFS].extend ); // get pointer on DEVFS context in cluster 0 devfs_ctx_t * devfs_ctx = hal_remote_lpt( extend_xp ); devfs_dev_inode_xp = hal_remote_lwd( XPTR( 0 , &devfs_ctx->dev_inode_xp ) ); devfs_external_inode_xp = hal_remote_lwd( XPTR( 0 , &devfs_ctx->external_inode_xp ) ); // populate DEVFS in all clusters devfs_local_init( devfs_dev_inode_xp, devfs_external_inode_xp, &devfs_internal_inode_xp ); } ///////////////////////////////////////////////////////////////////////////////// if( core_lid == 0 ) remote_barrier( XPTR( 0 , &global_barrier ), (info->x_size * info->y_size) ); barrier_wait( &local_barrier , info->cores_nr ); ///////////////////////////////////////////////////////////////////////////////// #if DEBUG_KERNEL_INIT if( (core_lid == 0) & (local_cxy == 0) ) printk("\n[DBG] %s : exit barrier 7 : dev_root = %l in cluster 0 / cycle %d\n", __FUNCTION__, devfs_dev_inode_xp , (uint32_t)hal_get_cycles() ); #endif ///////////////////////////////////////////////////////////////////////////////// // STEP 8 : CP0 in cluster 0 creates the first user process (process_init) ///////////////////////////////////////////////////////////////////////////////// if( (core_lid == 0) && (local_cxy == 0) ) { #if( DEBUG_KERNEL_INIT & 1 ) vfs_display( vfs_root_inode_xp ); #endif process_init_create(); } ///////////////////////////////////////////////////////////////////////////////// if( core_lid == 0 ) remote_barrier( XPTR( 0 , &global_barrier ), (info->x_size * info->y_size) ); barrier_wait( &local_barrier , info->cores_nr ); ///////////////////////////////////////////////////////////////////////////////// #if DEBUG_KERNEL_INIT if( (core_lid == 0) & (local_cxy == 0) ) printk("\n[DBG] %s : exit barrier 8 : process init created / cycle %d\n", __FUNCTION__ , (uint32_t)hal_get_cycles() ); #endif #if (DEBUG_KERNEL_INIT & 1) if( (core_lid == 0) & (local_cxy == 0) ) sched_display( 0 ); #endif ///////////////////////////////////////////////////////////////////////////////// // STEP 9 : CP0 in cluster 0 print banner ///////////////////////////////////////////////////////////////////////////////// if( (core_lid == 0) && (local_cxy == 0) ) { print_banner( (info->x_size * info->y_size) , info->cores_nr ); #if( DEBUG_KERNEL_INIT & 1 ) printk("\n\n***** memory fooprint for main kernel objects\n\n" " - thread descriptor : %d bytes\n" " - process descriptor : %d bytes\n" " - cluster manager : %d bytes\n" " - chdev descriptor : %d bytes\n" " - core descriptor : %d bytes\n" " - scheduler : %d bytes\n" " - rpc fifo : %d bytes\n" " - page descriptor : %d bytes\n" " - mapper root : %d bytes\n" " - ppm manager : %d bytes\n" " - kcm manager : %d bytes\n" " - khm manager : %d bytes\n" " - vmm manager : %d bytes\n" " - gpt root : %d bytes\n" " - list item : %d bytes\n" " - xlist item : %d bytes\n" " - spinlock : %d bytes\n" " - remote spinlock : %d bytes\n" " - rwlock : %d bytes\n" " - remote rwlock : %d bytes\n", sizeof( thread_t ), sizeof( process_t ), sizeof( cluster_t ), sizeof( chdev_t ), sizeof( core_t ), sizeof( scheduler_t ), sizeof( remote_fifo_t ), sizeof( page_t ), sizeof( mapper_t ), sizeof( ppm_t ), sizeof( kcm_t ), sizeof( khm_t ), sizeof( vmm_t ), sizeof( gpt_t ), sizeof( list_entry_t ), sizeof( xlist_entry_t ), sizeof( spinlock_t ), sizeof( remote_spinlock_t ), sizeof( rwlock_t ), sizeof( remote_rwlock_t )); #endif } // each core activates its private TICK IRQ dev_pic_enable_timer( CONFIG_SCHED_TICK_MS_PERIOD ); #if DEBUG_KERNEL_INIT printk("\n[DBG] %s : thread %x on core[%x,%d] jumps to thread_idle_func() / cycle %d\n", __FUNCTION__ , CURRENT_THREAD , local_cxy , core_lid , (uint32_t)hal_get_cycles() ); #endif // each core jump to thread_idle_func thread_idle_func(); }