Changeset 127
- Timestamp:
- Jul 3, 2017, 3:00:02 PM (7 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/kernel/kern/kernel_init.c
r124 r127 1 1 /* 2 2 * kernel_init.c - kernel parallel initialization 3 * 3 * 4 4 * Authors : Mohamed Lamine Karaoui (2015) 5 5 * Alain Greiner (2016,2017) … … 64 64 // They are initialised by the kernel_init() function. 65 65 // 66 // WARNING : The section names have been defined to control the base addresses of the 66 // WARNING : The section names have been defined to control the base addresses of the 67 67 // boot_info structure and the idle thread descriptors, through the kernel.ld script: 68 // - the boot_info structure is buil dby the bootloader, and used by kernel_init.69 // it must be first object in the kdata segment.68 // - the boot_info structure is built by the bootloader, and used by kernel_init. 69 // it must be the first object in the kdata segment. 70 70 // - the array of idle threads descriptors must be placed on the first page boundary after 71 71 // the boot_info structure in the kdata segment. … … 81 81 CONFIG_MAX_LOCAL_CORES] CONFIG_PPM_PAGE_ALIGNED; 82 82 83 // This variable defines the local cluster manager 83 // This variable defines the local cluster manager 84 84 __attribute__((section(".kdata"))) 85 85 cluster_t cluster_manager CONFIG_CACHE_LINE_ALIGNED; … … 105 105 cxy_t local_cxy CONFIG_CACHE_LINE_ALIGNED; 106 106 107 // This variable defines the TXT0 chdev descriptor 107 // This variable defines the TXT0 chdev descriptor 108 108 __attribute__((section(".kdata"))) 109 109 chdev_t txt0_chdev CONFIG_CACHE_LINE_ALIGNED; 110 110 111 // This variable is used for CP0 cores sy chronisation in kernel_init()111 // This variable is used for CP0 cores synchronisation in kernel_init() 112 112 __attribute__((section(".kdata"))) 113 113 remote_barrier_t global_barrier CONFIG_CACHE_LINE_ALIGNED; 114 114 115 // This variable is used for local cores sy chronisation in kernel_init()115 // This variable is used for local cores synchronisation in kernel_init() 116 116 __attribute__((section(".kdata"))) 117 117 barrier_t local_barrier CONFIG_CACHE_LINE_ALIGNED; 118 118 119 // This variable defines the array of supported File System contexts 119 // This variable defines the array of supported File System contexts 120 120 __attribute__((section(".kdata"))) 121 121 vfs_ctx_t fs_context[FS_TYPES_NR] CONFIG_CACHE_LINE_ALIGNED; … … 126 126 /////////////////////////////////////////////////////////////////////////////////////////// 127 127 static void print_banner( uint32_t nclusters , uint32_t ncores ) 128 { 128 { 129 129 printk("\n" 130 130 " _ __ __ _____ ______ __ __ _ __ _ _ \n" … … 137 137 " /_/ \\_\\ |______| |_| |_| \\_____/ |______/ |_| |_| |_| \\_\\ |_| |_| \n" 138 138 "\n\n\t\t Advanced Locality Management Operating System / Multi Kernel Hybrid\n" 139 "\n\n\t\t\t Version 0.0 : %d clusters / %d cores per cluster\n\n", nclusters , ncores ); 139 "\n\n\t\t\t Version 0.0 : %d clusters / %d cores per cluster\n\n", nclusters , ncores ); 140 140 } 141 141 142 142 143 143 /////////////////////////////////////////////////////////////////////////////////////////// 144 // This static function initializes the TXT0 chdev descriptor, associated to the "kernel 145 // terminal", and shared by all kernel instances for debug messages. It also register it144 // This static function initializes the TXT0 chdev descriptor, associated to the "kernel 145 // terminal", and shared by all kernel instances for debug messages. It also registers it 146 146 // in the chdev directory, containing extended pointers on all chdevs. 147 147 // The global variable txt0_chdev is replicated in all clusters, but only the chdev 148 148 // allocated in I/O cluster is used by ALMOS-MKH. 149 149 // Therefore, this function must be called by a thread running in the I/O cluster. 150 // As this TXT0 chdev supports only the TXT_SYNC_WRITE command, 151 // a server thread, 150 // As this TXT0 chdev supports only the TXT_SYNC_WRITE command, we don't create 151 // a server thread, we don't allocate a WTI, and we don't initialize the waiting queue. 152 152 /////////////////////////////////////////////////////////////////////////////////////////// 153 153 // @ info : pointer on the local boot-info structure. … … 156 156 { 157 157 boot_device_t * dev_tbl; // pointer on array of devices in boot_info 158 159 158 uint32_t dev_nr; // actual number of devices in this cluster 159 xptr_t base; // remote pointer on segment base 160 160 uint32_t type; // peripheral type 161 uint32_t func; // device function nal index161 uint32_t func; // device functional index 162 162 uint32_t impl; // device implementation index 163 164 165 163 uint32_t i; // device index in dev_tbl 164 uint32_t x; // X cluster coordinate 165 uint32_t y; // Y cluster coordinate 166 166 167 167 // get number of peripherals and base of devices array from boot_info 168 dev_nr= info->ext_dev_nr;168 dev_nr = info->ext_dev_nr; 169 169 dev_tbl = info->ext_dev; 170 170 171 171 // loop on external peripherals to find TXT device 172 173 172 for( i = 0 ; i < dev_nr ; i++ ) 173 { 174 174 base = dev_tbl[i].base; 175 175 type = dev_tbl[i].type; … … 177 177 impl = IMPL_FROM_TYPE( type ); 178 178 179 if (func == DEV_FUNC_TXT ) 179 if (func == DEV_FUNC_TXT ) 180 180 { 181 181 // initialize basic fields … … 210 210 } 211 211 212 212 kinit_dmsg("\n[INFO] %s : core[%x][0] created TXT0 chdev" 213 213 " / paddr = %l at cycle %d\n", 214 214 __FUNCTION__ , local_cxy , chdev_func_str( func ), 215 215 XPTR(local_cxy , &txt0_chdev) , hal_get_cycles() ); 216 216 } 217 218 } // end loop on devices 219 220 } // end txt0_device_init() 217 } 218 } 221 219 222 220 /////////////////////////////////////////////////////////////////////////////////////////// … … 233 231 { 234 232 boot_device_t * dev; // pointer on boot_info device (ICU/MMC/DMA) 235 236 237 233 uint32_t x; // X cluster coordinate 234 uint32_t y; // Y cluster coordinate 235 chdev_t * chdev_ptr; // local pointer on chdev descriptor 238 236 xptr_t chdev_xp; // extended pointer on chdev descriptor 239 237 … … 244 242 assert( ((info->cores_nr == 0) || (dev->channels != 0)) , __FUNCTION__ , 245 243 "ICU device must exist in cluster containing cores" ); 246 244 247 245 assert( (dev->channels == 1) , __FUNCTION__ , 248 246 "channels number must be 1 for ICU device" ); … … 259 257 260 258 assert( (chdev_ptr != NULL) , __FUNCTION__ , "cannot allocate ICU chdev" ); 261 262 // get extended pointer on chdev descriptor 259 260 // get extended pointer on chdev descriptor 263 261 chdev_xp = XPTR( local_cxy , chdev_ptr ); 264 262 265 // make ICU specific initialisation 263 // make ICU specific initialisation 266 264 // TODO remove these three parameters 267 265 dev_icu_init( chdev_ptr , dev->param0 , dev->param1 , dev->param2 ); 268 266 269 267 // initialize the ICU field in the chdev_dir[x][y] structures 270 // replicated in all clusters, and containing extended pointers 271 // on all remotely accessible devices 268 // replicated in all clusters, and containing extended pointers 269 // on all remotely accessible devices 272 270 for( x = 0 ; x < info->x_size ; x++ ) 273 271 { … … 280 278 281 279 // initialize the entries of the local chdev_icu_input structure 282 // defining how internal peripherals are connected to ICU 280 // defining how internal peripherals are connected to ICU 283 281 uint32_t id; 284 282 uint8_t valid; … … 304 302 __FUNCTION__ , local_cxy , hal_get_cycles() ); 305 303 306 /////////// MMC internal chdev /////////// 304 /////////// MMC internal chdev /////////// 307 305 308 306 dev = &info->dev_mmc; … … 310 308 if( dev->channels != 0 ) // MMC device is defined 311 309 { 312 assert( (dev->channels == 1) , __FUNCTION__ , 310 assert( (dev->channels == 1) , __FUNCTION__ , 313 311 "channels number must be 1 for MMC device" ); 314 312 … … 324 322 325 323 assert( (chdev_ptr != NULL) , __FUNCTION__ , "cannot allocate MMC chdev" ); 326 327 // get extended pointer on chdev descriptor 324 325 // get extended pointer on chdev descriptor 328 326 chdev_xp = XPTR( local_cxy , chdev_ptr ); 329 327 330 328 // make MMC specific initialisation 331 dev_mmc_init( chdev_ptr ); 329 dev_mmc_init( chdev_ptr ); 332 330 333 331 // initialize the MMC field in the chdev_dir[x][y] structures 334 // replicated in all clusters, and containing extended pointers 335 // on all remotely accessible devices 332 // replicated in all clusters, and containing extended pointers 333 // on all remotely accessible devices 336 334 for( x = 0 ; x < info->x_size ; x++ ) 337 335 { … … 359 357 uint32_t channel; 360 358 for( channel = 0 ; channel < dev->channels ; channel++ ) 361 { 359 { 362 360 chdev_ptr = chdev_create( FUNC_FROM_TYPE( dev->type ), 363 361 IMPL_FROM_TYPE( dev->type ), … … 367 365 368 366 assert( (chdev_ptr != NULL) , __FUNCTION__ , "cannot allocate DMA chdev" ); 369 370 // get extended pointer on channel descriptor 367 368 // get extended pointer on channel descriptor 371 369 chdev_xp = XPTR( local_cxy , chdev_ptr ); 372 370 373 371 // make DMA specific initialisation 374 dev_dma_init( chdev_ptr ); 372 dev_dma_init( chdev_ptr ); 375 373 376 374 // initialize only the DMA[channel] field in the local chdev_dir[x][y] … … 381 379 __FUNCTION__ , local_cxy , channel , hal_get_cycles() ); 382 380 } 383 } 381 } 384 382 } // end internal_devices_init() 385 383 386 384 /////////////////////////////////////////////////////////////////////////////////////////// 387 // This static function allocates memory for the chdev descriptors associated 385 // This static function allocates memory for the chdev descriptors associated 388 386 // to the external (shared) peripherals contained in the local cluster. These external 389 // devices (IOB, IOC, TXT, NIC, etc ) are distributed on all clusters. 387 // devices (IOB, IOC, TXT, NIC, etc ) are distributed on all clusters. 390 388 // It initialises these device descriptors as specified by the boot_info_t structure, 391 389 // including the dynamic linking with the driver for the specified implementation. 392 390 // Finally, all copies of the devices directory are initialised. 393 391 // 394 // The number of channel_devices depends on the device function nal type.395 // There isthree nested loops to build the full set of external channel_devices:392 // The number of channel_devices depends on the device functional type. 393 // There are three nested loops to build the full set of external channel_devices: 396 394 // - loop on external devices. 397 395 // - loop on channels for multi-channels devices. … … 400 398 // to select the cluster containing a given chdev[func,channel,direction]. 401 399 // All clusters scan the full set of chdevs, but only the cluster matching 402 // (chdev_gid % (x_size*y_size)) create the corresponding chdev. 400 // (chdev_gid % (x_size*y_size)) create the corresponding chdev. 403 401 // 404 402 // TODO check that cluster IO contains a PIC [AG] … … 410 408 { 411 409 boot_device_t * dev_tbl; // pointer on array of devices in boot_info 412 413 410 uint32_t dev_nr; // actual number of devices in this cluster 411 xptr_t base; // remote pointer on segment base 414 412 uint32_t type; // peripheral type 415 413 uint32_t func; // device functionnal index 416 414 uint32_t impl; // device implementation index 417 418 419 420 421 422 uint32_t directions_nr; // number of directions 423 424 425 426 427 415 uint32_t i; // device index in dev_tbl 416 uint32_t x; // X cluster coordinate 417 uint32_t y; // Y cluster coordinate 418 uint32_t channels_nr; // number of channels 419 uint32_t channel; // channel index 420 uint32_t directions_nr; // number of directions 421 uint32_t direction; // direction index 422 uint32_t p0; // device parameter 0 423 uint32_t p1; // device parameter 1 424 uint32_t p2; // device parameter 2 425 uint32_t p3; // device parameter 3 428 426 uint32_t first_channel; // used in loop on channels 429 427 430 428 chdev_t * chdev; // local pointer on one channel_device descriptor 431 429 xptr_t chdev_xp; // extended pointer on channel_device descriptor 432 430 uint32_t chdev_gid = 0; // global index of channel_device descriptor 433 431 434 432 // get number of peripherals and base of devices array from boot_info 435 dev_nr= info->ext_dev_nr;433 dev_nr = info->ext_dev_nr; 436 434 dev_tbl = info->ext_dev; 437 435 438 436 // loop on external peripherals 439 440 437 for( i = 0 ; i < dev_nr ; i++ ) 438 { 441 439 base = dev_tbl[i].base; 442 440 type = dev_tbl[i].type; … … 461 459 if( func == DEV_FUNC_ROM ) continue; 462 460 463 // check external device function nal type461 // check external device functional type 464 462 if( (func != DEV_FUNC_IOB) && 465 463 (func != DEV_FUNC_PIC) && … … 467 465 (func != DEV_FUNC_TXT) && 468 466 (func != DEV_FUNC_NIC) && 469 (func != DEV_FUNC_FBF) ) 467 (func != DEV_FUNC_FBF) ) 470 468 { 471 469 assert( false , __FUNCTION__ , "undefined external peripheral type" ); 472 470 } 473 471 474 // loops on channels 472 // loops on channels 475 473 for( channel = first_channel ; channel < channels_nr ; channel++ ) 476 { 474 { 477 475 // loop on directions 478 476 for( direction = 0 ; direction < directions_nr ; direction++ ) … … 494 492 base ); 495 493 496 assert( (chdev != NULL), __FUNCTION__ , 494 assert( (chdev != NULL), __FUNCTION__ , 497 495 "cannot allocate external device" ); 498 496 … … 514 512 } 515 513 516 // all external (shared) devices are remotely accessible 514 // all external (shared) devices are remotely accessible 517 515 // initialize the replicated chdev_dir[x][y] structures 518 // defining the extended pointers on chdev descriptors 519 xptr_t * entry; 520 516 // defining the extended pointers on chdev descriptors 517 xptr_t * entry; 518 521 519 if( func == DEV_FUNC_IOB ) entry = &chdev_dir.iob; 522 520 if( func == DEV_FUNC_PIC ) entry = &chdev_dir.pic; … … 525 523 if( func == DEV_FUNC_FBF ) entry = &chdev_dir.fbf[channel]; 526 524 if( func == DEV_FUNC_NIC ) entry = &chdev_dir.nic_tx[channel]; 527 525 528 526 for( x = 0 ; x < info->x_size ; x++ ) 529 527 { … … 535 533 } 536 534 537 535 kinit_dmsg("\n[INFO] %s : core[%x][0] create chdev %s[%d] at cycle %d\n", 538 536 __FUNCTION__ , local_cxy , chdev_func_str( func ), 539 537 channel , hal_get_cycles() ); … … 549 547 550 548 // initialize the entries of the local chdev_pic_input structure 551 // defining how external peripherals are connected to PIC 552 if( func == DEV_FUNC_PIC ) 549 // defining how external peripherals are connected to PIC 550 if( func == DEV_FUNC_PIC ) 553 551 { 554 552 uint32_t id; … … 577 575 else if( dev_func == DEV_FUNC_IOC ) 578 576 { 579 index = &chdev_pic_input.ioc[channel]; 577 index = &chdev_pic_input.ioc[channel]; 580 578 } 581 579 else if( (dev_func == DEV_FUNC_NIC) && (is_rx == 0) ) 582 580 { 583 index = &chdev_pic_input.nic_tx[channel]; 581 index = &chdev_pic_input.nic_tx[channel]; 584 582 } 585 583 else if( (dev_func == DEV_FUNC_NIC) && (is_rx != 0) ) 586 584 { 587 index = &chdev_pic_input.nic_rx[channel]; 585 index = &chdev_pic_input.nic_rx[channel]; 588 586 } 589 587 else 590 588 { 591 589 assert( false , __FUNCTION__ , "illegal source device for PIC input" ); 592 } 590 } 593 591 594 592 // set entry in local structure 595 *index = id; 593 *index = id; 596 594 } 597 595 } // end loop on PIC inputs 598 596 } // end PIC 599 597 } // end loop on devices 600 598 } // end external_devices_init() 601 599 … … 615 613 gid_t * gid ) 616 614 { 617 615 uint32_t i; 618 616 gid_t global_id; 619 617 620 618 // get global identifier from hardware register 621 global_id = hal_get_gid(); 619 global_id = hal_get_gid(); 622 620 623 621 // makes an associative search in boot_info to get (cxy,lid) from global_id … … 649 647 lid_t core_lid = -1; // running core local index 650 648 cxy_t core_cxy = -1; // running core cluster identifier 651 gid_t core_gid; // running core hardware identifier 649 gid_t core_gid; // running core hardware identifier 652 650 cluster_t * cluster; // pointer on local cluster manager 653 651 core_t * core; // pointer on running core descriptor … … 661 659 &core_gid ); 662 660 663 // CP0 initiali secluster identifier661 // CP0 initializes cluster identifier 664 662 if( core_lid == 0 ) local_cxy = info->cxy; 665 663 666 // each core get pointer on its private idle thread descriptor667 668 669 // each core registers this thread pointer in hardware register 664 // each core gets a pointer on its private idle thread descriptor 665 thread = (thread_t *)( idle_threads + (core_lid * CONFIG_THREAD_DESC_SIZE) ); 666 667 // each core registers this thread pointer in hardware register 670 668 hal_set_current_thread( thread ); 671 669 … … 673 671 xlist_root_init( XPTR( local_cxy, &thread->xlocks_root ) ); 674 672 675 // CP0 in I/O cluster initiali ses TXT0 chdev descriptor673 // CP0 in I/O cluster initializes TXT0 chdev descriptor 676 674 if( (core_lid == 0) && (core_cxy == info->io_cxy) ) txt0_device_init( info ); 677 675 678 676 ///////////////////////////////////////////////////////////////////////////////// 679 677 // global & local synchro to protect access to TXT0 terminal 680 if( core_lid == 0 ) remote_barrier( XPTR( info->io_cxy , &global_barrier ), 678 if( core_lid == 0 ) remote_barrier( XPTR( info->io_cxy , &global_barrier ), 681 679 (info->x_size * info->y_size) ); 682 680 barrier_wait( &local_barrier , info->cores_nr ); 683 681 ///////////////////////////////////////////////////////////////////////////////// 684 682 685 if( (core_lid == 0) && (local_cxy == info->io_cxy) ) 683 if( (core_lid == 0) && (local_cxy == info->io_cxy) ) 686 684 { 687 685 kinit_dmsg("\n[INFO] %s : core[%x][%d] exit barrier 0 at cycle %d\n", … … 713 711 ///////////////////////////////////////////////////////////////////////////////// 714 712 // global & local synchro, to protect access to cluster manager 715 if( core_lid == 0 ) remote_barrier( XPTR( info->io_cxy , &global_barrier ), 713 if( core_lid == 0 ) remote_barrier( XPTR( info->io_cxy , &global_barrier ), 716 714 (info->x_size * info->y_size) ); 717 715 barrier_wait( &local_barrier , info->cores_nr ); 718 716 ///////////////////////////////////////////////////////////////////////////////// 719 717 720 if( (core_lid == 0) && (local_cxy == info->io_cxy) ) 718 if( (core_lid == 0) && (local_cxy == info->io_cxy) ) 721 719 { 722 720 kinit_dmsg("\n[INFO] %s : core[%x][%d] exit barrier 1 at cycle %d\n", … … 726 724 // all cores get pointer on local cluster manager and on core descriptor 727 725 cluster = &cluster_manager; 728 726 core = &cluster->core_tbl[core_lid]; 729 727 730 728 // CP0 initializes the process_zero descriptor … … 735 733 #endif 736 734 737 // CP0 allocates and initiali ses the internal peripheral chdev descriptors.735 // CP0 allocates and initializes the internal peripheral chdev descriptors. 738 736 // Each CP0[cxy] scan the set of its internal (private) peripherals, 739 // and allocate memory for the corresponding chdev descriptors.737 // and allocates memory for the corresponding chdev descriptors. 740 738 if( core_lid == 0 ) internal_devices_init( info ); 741 742 // CP0 allocates one WTI mailb box per core for Inter Processor Interrupt739 740 // CP0 allocates one WTI mailbox per core for Inter Processor Interrupt 743 741 // this must be done after ICU chdev initialisation, by CP0 only, and before 744 742 // external devices initialisation to enforce the rule : … … 766 764 // Each CP0[cxy] scan the set of external (shared) peripherals (but the TXT0), 767 765 // and allocates memory for the chdev descriptors that must be placed 768 // on the (cxy) cluster according to the global index value. 766 // on the (cxy) cluster according to the global index value. 769 767 if( core_lid == 0 ) external_devices_init( info ); 770 768 771 769 ///////////////////////////////////////////////////////////////////////////////// 772 770 // global &local synchro to protect access to peripherals 773 if( core_lid == 0 ) remote_barrier( XPTR( info->io_cxy , &global_barrier ), 771 if( core_lid == 0 ) remote_barrier( XPTR( info->io_cxy , &global_barrier ), 774 772 (info->x_size * info->y_size) ); 775 773 barrier_wait( &local_barrier , info->cores_nr ); 776 774 ///////////////////////////////////////////////////////////////////////////////// 777 775 778 if( (core_lid == 0) && (local_cxy == info->io_cxy) ) 779 { 780 kinit_dmsg("\n[INFO] %s : core[%x][%d] exit barrier 2 at cycle %d\n", 776 if( (core_lid == 0) && (local_cxy == info->io_cxy) ) 777 { 778 kinit_dmsg("\n[INFO] %s : core[%x][%d] exit barrier 2 at cycle %d\n", 781 779 __FUNCTION__ , core_cxy , core_lid , hal_get_cycles() ); 782 780 } 783 781 784 782 error = thread_kernel_init( thread, 785 THREAD_IDLE, 786 &thread_idle_func, 783 THREAD_IDLE, 784 &thread_idle_func, 787 785 NULL, 788 786 core_lid ); … … 800 798 thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_GLOBAL ); 801 799 802 if( (core_lid == 0) && (local_cxy == info->io_cxy) ) 800 if( (core_lid == 0) && (local_cxy == info->io_cxy) ) 803 801 { 804 802 kinit_dmsg("\n[INFO] %s : core[%x][%d] created idle thread %x at cycle %d\n", … … 806 804 } 807 805 808 // CP0 in all clusters initializes cooperatively VFS and DEVFS 806 // CP0 in all clusters initializes cooperatively VFS and DEVFS 809 807 if( (core_lid == 0) ) 810 808 { … … 834 832 835 833 // mount the DEVFS File system 836 834 devfs_mount( root_inode_xp , "dev" ); 837 835 } 838 836 839 837 840 838 // CP0 in I/O cluster creates the process_init and print banner 841 if( (core_lid == 0) && (local_cxy == info->io_cxy) ) 839 if( (core_lid == 0) && (local_cxy == info->io_cxy) ) 842 840 { 843 841 process_init_create(); … … 866 864 " - rwlock : %d bytes\n" 867 865 " - remote rwlock : %d bytes\n", 868 sizeof( thread_t ), 866 sizeof( thread_t ), 869 867 sizeof( process_t ), 870 868 sizeof( cluster_t ), … … 895 893 ///////////////////////////////////////////////////////////////////////////////// 896 894 897 if( (core_lid == 0) && (local_cxy == info->io_cxy) ) 898 { 899 kinit_dmsg("\n[INFO] %s : core[%x][%d] exit barrier 3 at cycle %d\n", 895 if( (core_lid == 0) && (local_cxy == info->io_cxy) ) 896 { 897 kinit_dmsg("\n[INFO] %s : core[%x][%d] exit barrier 3 at cycle %d\n", 900 898 __FUNCTION__ , core_cxy , core_lid , hal_get_cycles() ); 901 899 } 902 900 903 // each core activates its private PTI IRQ 901 // each core activates its private PTI IRQ 904 902 dev_icu_set_period( core_lid , CONFIG_SCHED_TICK_PERIOD ); 905 903 dev_icu_enable_irq( core_lid , PTI_TYPE , core_lid , NULL ); … … 918 916 // each core jump to idle thread 919 917 thread_idle_func(); 920 921 } // end kernel_init() 922 918 } 919
Note: See TracChangeset
for help on using the changeset viewer.