/* * 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 #define KERNEL_INIT_SYNCHRO 0xA5A5B5B5 /////////////////////////////////////////////////////////////////////////////////////////// // 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 __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; /////////////////////////////////////////////////////////////////////////////////////////// // 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\t Version 0.0 : %d cluster(s) / %d core(s) per cluster\n\n", 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) , __FUNCTION__ , "numner of TXT channels cannot be 0\n"); // initializes TXT0 basic fields 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[0] ) , XPTR( local_cxy , &txt0_chdev ) ); } } kinit_dmsg("\n[INFO] %s created TXT0 chdev in cluster %x at cycle %d\n", __FUNCTION__ , local_cxy , (uint32_t)hal_time_stamp() ); } } // 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) , __FUNCTION__ , "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) , __FUNCTION__ , "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 ) ); } } kinit_dmsg("\n[INFO] %s created MMC in cluster %x / chdev = %x\n", __FUNCTION__ , channel , local_cxy , chdev_ptr ); } /////////////////////////////// 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) , __FUNCTION__ , "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 ); kinit_dmsg("\n[INFO] %s created DMA[%d] in cluster %x / chdev = %x\n", __FUNCTION__ , channel , local_cxy , chdev_ptr ); } } } } // 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, // including 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: In each cluster, the local CP0 core // computes the global index for all external chdevs, and creates only the chdevs that // must be placed in the local cluster. // 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) uint32_t first_channel; // used in loop on channels for TXT 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 if (func == DEV_FUNC_NIC) directions = 2; else directions = 1; // The TXT0 chdev has already been created if (func == DEV_FUNC_TXT) first_channel = 1; else first_channel = 0; // do nothing for RO, 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 ) , __FUNCTION__ , "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) ) , __FUNCTION__ , "undefined external peripheral type\n" ); // loops on channels for( channel = first_channel ; channel < channels ; channel++ ) { // loop on directions for( rx = 0 ; rx < directions ; rx++ ) { // 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 // if local cluster matches target cluster if( target_cxy == local_cxy ) { chdev = chdev_create( func, impl, channel, rx, // direction base ); assert( (chdev != NULL), __FUNCTION__ , "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_TXT ) entry = &chdev_dir.txt[channel]; if(func==DEV_FUNC_FBF ) entry = &chdev_dir.fbf[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 ) ); } } kinit_dmsg("\n[INFO] %s create chdev %s[%d] in cluster %x / chdev = %x\n", __FUNCTION__ , chdev_func_str( func ), channel , local_cxy , chdev ); } // 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. // This initialisation must be done before other devices initialisation because the IRQ // routing infrastructure is required for internal and external devices initialisation. /////////////////////////////////////////////////////////////////////////////////////////// // @ 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 , __FUNCTION__ , "PIC device not found\n" ); // allocate and initialize the PIC chdev in local cluster chdev = chdev_create( func, impl, 0, // channel 0, // direction, base ); assert( (chdev != NULL), __FUNCTION__ , "no memory for PIC chdev\n" ); // make PIC device type specific initialisation dev_pic_init( chdev ); // register extended pointer on PIC chdev in "chdev_dir" array in all clusters 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 the "iopic_input" structure // defining how external IRQs are connected to IOPIC uint32_t id; uint8_t valid; uint32_t type; uint8_t channel; uint8_t is_rx; 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; if( valid ) // only valid inputs are registered { uint32_t * index; // local pointer on one entry uint16_t func = FUNC_FROM_TYPE( type ); if ( func == DEV_FUNC_TXT ) index = &iopic_input.txt[channel]; else if( func == DEV_FUNC_IOC ) index = &iopic_input.ioc[channel]; else if( (func == DEV_FUNC_NIC) && (is_rx == 0) ) index = &iopic_input.nic_tx[channel]; else if( (func == DEV_FUNC_NIC) && (is_rx != 0) ) index = &iopic_input.nic_rx[channel]; else if( func == DEV_FUNC_IOB ) index = &iopic_input.iob; else assert( false , __FUNCTION__ , "illegal source device for IOPIC input" ); // set entry in local structure *index = id; } } kinit_dmsg("\n[INFO] %s created PIC chdev in cluster %x at cycle %d\n", __FUNCTION__ , local_cxy , (uint32_t)hal_time_stamp() ); } // 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 , __FUNCTION__ , "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 display on TXT0 the content of the external chdev directory, // in the local cluster. //////////////////////////////////////////////////////////////////////////////////////////// static void chdev_dir_display( ) { cxy_t iob_cxy = GET_CXY( chdev_dir.iob ); chdev_t * iob_ptr = (chdev_t *)GET_PTR( chdev_dir.iob ); xptr_t iob_base = hal_remote_lwd( XPTR( iob_cxy , &iob_ptr->base ) ); cxy_t pic_cxy = GET_CXY( chdev_dir.pic ); chdev_t * pic_ptr = (chdev_t *)GET_PTR( chdev_dir.pic ); xptr_t pic_base = hal_remote_lwd( XPTR( pic_cxy , &pic_ptr->base ) ); cxy_t txt0_cxy = GET_CXY( chdev_dir.txt[0] ); chdev_t * txt0_ptr = (chdev_t *)GET_PTR( chdev_dir.txt[0] ); xptr_t txt0_base = hal_remote_lwd( XPTR( txt0_cxy , &txt0_ptr->base ) ); cxy_t txt1_cxy = GET_CXY( chdev_dir.txt[1] ); chdev_t * txt1_ptr = (chdev_t *)GET_PTR( chdev_dir.txt[1] ); xptr_t txt1_base = hal_remote_lwd( XPTR( txt1_cxy , &txt1_ptr->base ) ); cxy_t txt2_cxy = GET_CXY( chdev_dir.txt[2] ); chdev_t * txt2_ptr = (chdev_t *)GET_PTR( chdev_dir.txt[2] ); xptr_t txt2_base = hal_remote_lwd( XPTR( txt2_cxy , &txt2_ptr->base ) ); cxy_t ioc_cxy = GET_CXY( chdev_dir.ioc[0] ); chdev_t * ioc_ptr = (chdev_t *)GET_PTR( chdev_dir.ioc[0] ); xptr_t ioc_base = hal_remote_lwd( XPTR( ioc_cxy , &ioc_ptr->base ) ); cxy_t fbf_cxy = GET_CXY( chdev_dir.fbf[0] ); chdev_t * fbf_ptr = (chdev_t *)GET_PTR( chdev_dir.fbf[0] ); xptr_t fbf_base = hal_remote_lwd( XPTR( fbf_cxy , &fbf_ptr->base ) ); cxy_t nic_rx_cxy = GET_CXY( chdev_dir.nic_rx[0] ); chdev_t * nic_rx_ptr = (chdev_t *)GET_PTR( chdev_dir.nic_rx[0] ); xptr_t nic_rx_base = hal_remote_lwd( XPTR( nic_rx_cxy , &nic_rx_ptr->base ) ); cxy_t nic_tx_cxy = GET_CXY( chdev_dir.nic_tx[0] ); chdev_t * nic_tx_ptr = (chdev_t *)GET_PTR( chdev_dir.nic_tx[0] ); xptr_t nic_tx_base = hal_remote_lwd( XPTR( nic_tx_cxy , &nic_tx_ptr->base ) ); printk("\n*** external chdev directory in cluster %x\n" " - iob = %l / base = %l\n" " - pic = %l / base = %l\n" " - txt[0] = %l / base = %l\n" " - txt[1] = %l / base = %l\n" " - txt[2] = %l / base = %l\n" " - ioc[0] = %l / base = %l\n" " - fbf[0] = %l / base = %l\n" " - nic_rx[0] = %l / base = %l\n" " - nic_tx[0] = %l / base = %l\n", local_cxy, chdev_dir.iob, iob_base, chdev_dir.pic, pic_base, chdev_dir.txt[0], txt0_base, chdev_dir.txt[1], txt1_base, chdev_dir.txt[2], txt2_base, chdev_dir.ioc[0], ioc_base, chdev_dir.fbf[0], fbf_base, chdev_dir.nic_rx[0], nic_rx_base, chdev_dir.nic_tx[0], nic_tx_base ); } /////////////////////////////////////////////////////////////////////////////////////////// // 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; uint32_t status; // running core status register cxy_t io_cxy = info->io_cxy; ///////////////////////////////////////////////////////////////////////////////// // 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 initializes the idle thread "locks_root" and "xlocks_root" fields list_root_init( &thread->locks_root ); xlist_root_init( XPTR( local_cxy , &thread->xlocks_root ) ); // CP0 in I/O cluster initialises TXT0 chdev descriptor if( (core_lid == 0) && (core_cxy == io_cxy) ) txt0_device_init( info ); ///////////////////////////////////////////////////////////////////////////////// if( core_lid == 0 ) remote_barrier( XPTR( io_cxy , &global_barrier ), (info->x_size * info->y_size) ); barrier_wait( &local_barrier , info->cores_nr ); if( (core_lid == 0) && (local_cxy == 0) ) kinit_dmsg("\n[INFO] %s exit barrier 0 at cycle %d : TXT0 initialized\n", __FUNCTION__, (uint32_t)hal_time_stamp()); ///////////////////////////////////////////////////////////////////////////// // STEP 1 : all cores check its core identifier. // CP0 initializes the local cluster manager. // This includes the memory allocators. ///////////////////////////////////////////////////////////////////////////// // all cores check identifiers if( error ) { printk("\n[PANIC] in %s : illegal core identifiers" " gid = %x / cxy = %x / lid = %d\n", __FUNCTION__ , core_lid , core_cxy , core_lid ); hal_core_sleep(); } // CP0 initializes cluster manager if( core_lid == 0 ) { error = cluster_init( info ); if( error ) { printk("\n[PANIC] in %s : cannot initialise cluster %x", __FUNCTION__ , local_cxy ); hal_core_sleep(); } } ///////////////////////////////////////////////////////////////////////////////// if( core_lid == 0 ) remote_barrier( XPTR( io_cxy , &global_barrier ), (info->x_size * info->y_size) ); barrier_wait( &local_barrier , info->cores_nr ); ///////////////////////////////////////////////////////////////////////////////// if( (core_lid == 0) && (local_cxy == 0) ) kinit_dmsg("\n[INFO] %s exit barrier 1 at cycle %d : clusters initialised\n", __FUNCTION__, (uint32_t)hal_time_stamp()); ///////////////////////////////////////////////////////////////////////////////// // STEP 2 : all CP0s initialize the process_zero descriptor. // CP0 in cluster 0 initialises 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_reference_init( &process_zero , 0 , XPTR_NULL ); // 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( io_cxy , &global_barrier ), (info->x_size * info->y_size) ); barrier_wait( &local_barrier , info->cores_nr ); //////////////////////////////////////////////////////////////////////////////// if( (core_lid == 0) && (local_cxy == 0) ) kinit_dmsg("\n[INFO] %s exit barrier 2 at cycle %d : PIC initialised\n", __FUNCTION__, (uint32_t)hal_time_stamp()); //////////////////////////////////////////////////////////////////////////////// // STEP 3 : all CP0s complete the distibuted LAPIC initialization. // all CP0s initialize their internal chdev descriptors // all CP0s initialize their 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( io_cxy , &global_barrier ), (info->x_size * info->y_size) ); barrier_wait( &local_barrier , info->cores_nr ); ///////////////////////////////////////////////////////////////////////////////// if( (core_lid == 0) && (local_cxy == 0) ) kinit_dmsg("\n[INFO] %s exit barrier 3 at cycle %d : all chdev initialised\n", __FUNCTION__, (uint32_t)hal_time_stamp()); ///////////////////////////////////////////////////////////////////////////////// // 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. ///////////////////////////////////////////////////////////////////////////////// if( CONFIG_KINIT_DEBUG ) chdev_dir_display(); // All cores enable the shared IPI channel // @@@ hal_set_ebase( 0x1000 ); // @@@ dev_pic_enable_ipi(); hal_enable_irq( &status ); kinit_dmsg("\n[INFO] %s : IRQs enabled for core[%x,%d] / SR = %x\n", __FUNCTION__ , local_cxy , core_lid , hal_get_sr() ); // all cores create the idle thread descriptor error = thread_kernel_init( thread, THREAD_IDLE, &thread_idle_func, NULL, core_lid ); if( error ) { printk("\n[PANIC] in %s : core[%x][%d] cannot initialize idle thread\n", __FUNCTION__ , local_cxy , core_lid ); hal_core_sleep(); } // all cores register idle thread in scheduler core->scheduler.idle = thread; // all core activate the idle thread thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_GLOBAL ); if( (core_lid == 0) && (local_cxy == 0) ) { kinit_dmsg("\n[INFO] %s : created idle thread %x at cycle %d\n", __FUNCTION__ , thread , (uint32_t)hal_time_stamp()); } // 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. create FATFS context in cluster 0 fatfs_ctx_t * fatfs_ctx = fatfs_ctx_alloc(); assert( (fatfs_ctx != NULL) , __FUNCTION__ , "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) , __FUNCTION__ , "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 } else { printk("\n[PANIC] in %s : root FS must be FATFS\n", __FUNCTION__ ); hal_core_sleep(); } // register VFS root inode in process_zero 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( io_cxy , &global_barrier ), (info->x_size * info->y_size) ); barrier_wait( &local_barrier , info->cores_nr ); ///////////////////////////////////////////////////////////////////////////////// if( (core_lid == 0) && (local_cxy == 0) ) kinit_dmsg("\n[INFO] %s exit barrier 4 at cycle %d : VFS OK in cluster 0\n", __FUNCTION__, (uint32_t)hal_time_stamp()); ///////////////////////////////////////////////////////////////////////////////// // 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 ) { // allocate memory for FATFS context fatfs_ctx_t * fatfs_ctx = fatfs_ctx_alloc(); assert( (fatfs_ctx != NULL) , __FUNCTION__ , "cannot create FATFS context\n" ); // get local pointer on VFS context for FATFS vfs_ctx_t * vfs_ctx = &fs_context[FS_TYPE_FATFS]; // 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) ); // copy FATFS context from cluster 0 to local cluster hal_remote_memcpy( XPTR( local_cxy , fatfs_ctx ), XPTR( 0 , fatfs_ctx ), sizeof(fatfs_ctx_t) ); // update extend field in local copy of VFS context vfs_ctx->extend = fatfs_ctx; } // 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( io_cxy , &global_barrier ), (info->x_size * info->y_size) ); barrier_wait( &local_barrier , info->cores_nr ); ///////////////////////////////////////////////////////////////////////////////// // if( (core_lid == 0) && (local_cxy == 0) ) kinit_dmsg("\n[INFO] %s exit barrier 5 at cycle %d : VFS OK in all clusters\n", __FUNCTION__, (uint32_t)hal_time_stamp()); ///////////////////////////////////////////////////////////////////////////////// // 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 == io_cxy) ) { // 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) , __FUNCTION__ , "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 ); } printk("\n@@@ %s : cluster %x reach barrier 6\n", __FUNCTION__ , local_cxy ); ///////////////////////////////////////////////////////////////////////////////// if( core_lid == 0 ) remote_barrier( XPTR( io_cxy , &global_barrier ), (info->x_size * info->y_size) ); barrier_wait( &local_barrier , info->cores_nr ); ///////////////////////////////////////////////////////////////////////////////// // if( (core_lid == 0) && (local_cxy == 0) ) kinit_dmsg("\n[INFO] %s exit barrier 6 at cycle %d : DEVFS OK in cluster IO\n", __FUNCTION__, (uint32_t)hal_time_stamp()); ///////////////////////////////////////////////////////////////////////////////// // 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 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 IO xptr_t extend_xp = XPTR( io_cxy , &fs_context[FS_TYPE_DEVFS].extend ); // get pointer on DEVFS context in cluster IO devfs_ctx_t * devfs_ctx = hal_remote_lpt( extend_xp ); devfs_dev_inode_xp = hal_remote_lwd( XPTR( io_cxy , &devfs_ctx->dev_inode_xp ) ); devfs_external_inode_xp = hal_remote_lwd( XPTR( io_cxy , &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( io_cxy , &global_barrier ), (info->x_size * info->y_size) ); barrier_wait( &local_barrier , info->cores_nr ); ///////////////////////////////////////////////////////////////////////////////// if( (core_lid == 0) && (local_cxy == 0) ) kinit_dmsg("\n[INFO] %s exit barrier 7 at cycle %d : DEVFS OK in all clusters\n", __FUNCTION__, (uint32_t)hal_time_stamp()); #if CONFIG_KINIT_DEBUG vfs_display( vfs_root_inode_xp ); #endif ///////////////////////////////////////////////////////////////////////////////// // STEP 8 : CP0 in I/O cluster creates the first user process (process_init) ///////////////////////////////////////////////////////////////////////////////// if( (core_lid == 0) && (local_cxy == io_cxy) ) { process_init_create(); } ///////////////////////////////////////////////////////////////////////////////// if( core_lid == 0 ) remote_barrier( XPTR( info->io_cxy , &global_barrier ), (info->x_size * info->y_size) ); barrier_wait( &local_barrier , info->cores_nr ); ///////////////////////////////////////////////////////////////////////////////// if( (core_lid == 0) && (local_cxy == 0) ) kinit_dmsg("\n[INFO] %s exit barrier 8 at cycle %d : process init created\n", __FUNCTION__ , (uint32_t)hal_time_stamp() ); ///////////////////////////////////////////////////////////////////////////////// // STEP 9 : CP0 in cluster 0 print banner ///////////////////////////////////////////////////////////////////////////////// if( (core_lid == 0) && (local_cxy == io_cxy) ) { print_banner( (info->x_size * info->y_size) , info->cores_nr ); kinit_dmsg("\n\n*** memory fooprint for main kernet 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( rpc_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 )); } // each core activates its private TICK IRQ dev_pic_enable_timer( CONFIG_SCHED_TICK_PERIOD ); if( (core_lid == 0) && (local_cxy == io_cxy) ) thread_dmsg("\n[INFO] %s complete kernel init in cluster 0 at cycle %d\n" __FUNCTION__ , (uint32_t)hal_time_stamp() ) // each core jump to idle thread thread_idle_func(); }