/* This file contains the main program of MINIX as well as its shutdown code. * The routine main() initializes the system and starts the ball rolling by * setting up the process table, interrupt vectors, and scheduling each task * to run to initialize itself. * The routine shutdown() does the opposite and brings down MINIX. * * The entries into this file are: * main: MINIX main program * prepare_shutdown: prepare to take MINIX down */ #include "kernel/kernel.h" #include #include #include #include #include #include #include #include #include #include #include "clock.h" #include "direct_utils.h" #include "hw_intr.h" #include "arch_proto.h" #ifdef CONFIG_SMP #include "smp.h" #endif #ifdef USE_WATCHDOG #include "watchdog.h" #endif #include "spinlock.h" /* dummy for linking */ char *** _penviron; /* Prototype declarations for PRIVATE functions. */ static void announce(void); void bsp_finish_booting(void) { int i; #if SPROFILE sprofiling = 0; /* we're not profiling until instructed to */ #endif /* SPROFILE */ cprof_procs_no = 0; /* init nr of hash table slots used */ cpu_identify(); vm_running = 0; krandom.random_sources = RANDOM_SOURCES; krandom.random_elements = RANDOM_ELEMENTS; /* MINIX is now ready. All boot image processes are on the ready queue. * Return to the assembly code to start running the current process. */ /* it should point somewhere */ get_cpulocal_var(bill_ptr) = get_cpulocal_var_ptr(idle_proc); get_cpulocal_var(proc_ptr) = get_cpulocal_var_ptr(idle_proc); announce(); /* print MINIX startup banner */ /* * we have access to the cpu local run queue, only now schedule the processes. * We ignore the slots for the former kernel tasks */ for (i=0; i < NR_BOOT_PROCS - NR_TASKS; i++) { RTS_UNSET(proc_addr(i), RTS_PROC_STOP); } /* * enable timer interrupts and clock task on the boot CPU */ if (boot_cpu_init_timer(system_hz)) { panic("FATAL : failed to initialize timer interrupts, " "cannot continue without any clock source!"); } fpu_init(); /* Warnings for sanity checks that take time. These warnings are printed * so it's a clear warning no full release should be done with them * enabled. */ #if DEBUG_SCHED_CHECK FIXME("DEBUG_SCHED_CHECK enabled"); #endif #if DEBUG_VMASSERT FIXME("DEBUG_VMASSERT enabled"); #endif #if DEBUG_PROC_CHECK FIXME("PROC check enabled"); #endif DEBUGEXTRA(("cycles_accounting_init()... ")); cycles_accounting_init(); DEBUGEXTRA(("done\n")); #ifdef CONFIG_SMP cpu_set_flag(bsp_cpu_id, CPU_IS_READY); machine.processors_count = ncpus; machine.bsp_id = bsp_cpu_id; #else machine.processors_count = 1; machine.bsp_id = 0; #endif /* Kernel may no longer use bits of memory as VM will be running soon */ kernel_may_alloc = 0; switch_to_user(); NOT_REACHABLE; } /*===========================================================================* * kmain * *===========================================================================*/ void kmain(kinfo_t *local_cbi) { /* Start the ball rolling. */ struct boot_image *ip; /* boot image pointer */ register struct proc *rp; /* process pointer */ register int i, j; static int bss_test; /* bss sanity check */ assert(bss_test == 0); bss_test = 1; /* save a global copy of the boot parameters */ memcpy(&kinfo, local_cbi, sizeof(kinfo)); memcpy(&kmess, kinfo.kmess, sizeof(kmess)); /* We have done this exercise in pre_init so we expect this code to simply work! */ machine.board_id = get_board_id_by_name(env_get(BOARDVARNAME)); #ifdef __arm__ /* We want to initialize serial before we do any output */ arch_ser_init(); #endif /* We can talk now */ DEBUGBASIC(("MINIX booting\n")); /* Kernel may use bits of main memory before VM is started */ kernel_may_alloc = 1; assert(sizeof(kinfo.boot_procs) == sizeof(image)); memcpy(kinfo.boot_procs, image, sizeof(kinfo.boot_procs)); cstart(); BKL_LOCK(); DEBUGEXTRA(("main()\n")); proc_init(); if(NR_BOOT_MODULES != kinfo.mbi.mi_mods_count) panic("expecting %d boot processes/modules, found %d", NR_BOOT_MODULES, kinfo.mbi.mi_mods_count); /* Set up proc table entries for processes in boot image. */ for (i=0; i < NR_BOOT_PROCS; ++i) { int schedulable_proc; proc_nr_t proc_nr; int ipc_to_m, kcalls; sys_map_t map; ip = &image[i]; /* process' attributes */ DEBUGEXTRA(("initializing %s... ", ip->proc_name)); rp = proc_addr(ip->proc_nr); /* get process pointer */ ip->endpoint = rp->p_endpoint; /* ipc endpoint */ rp->p_cpu_time_left = 0; if(i < NR_TASKS) /* name (tasks only) */ strlcpy(rp->p_name, ip->proc_name, sizeof(rp->p_name)); if(i >= NR_TASKS) { /* Remember this so it can be passed to VM */ multiboot_module_t *mb_mod = &kinfo.module_list[i - NR_TASKS]; ip->start_addr = mb_mod->mod_start; ip->len = mb_mod->mod_end - mb_mod->mod_start; } reset_proc_accounting(rp); /* See if this process is immediately schedulable. * In that case, set its privileges now and allow it to run. * Only kernel tasks and the root system process get to run immediately. * All the other system processes are inhibited from running by the * RTS_NO_PRIV flag. They can only be scheduled once the root system * process has set their privileges. */ proc_nr = proc_nr(rp); schedulable_proc = (iskerneln(proc_nr) || isrootsysn(proc_nr) || proc_nr == VM_PROC_NR); if(schedulable_proc) { /* Assign privilege structure. Force a static privilege id. */ (void) get_priv(rp, static_priv_id(proc_nr)); /* Privileges for kernel tasks. */ if(proc_nr == VM_PROC_NR) { priv(rp)->s_flags = VM_F; priv(rp)->s_trap_mask = SRV_T; ipc_to_m = SRV_M; kcalls = SRV_KC; priv(rp)->s_sig_mgr = SELF; rp->p_priority = SRV_Q; rp->p_quantum_size_ms = SRV_QT; } else if(iskerneln(proc_nr)) { /* Privilege flags. */ priv(rp)->s_flags = (proc_nr == IDLE ? IDL_F : TSK_F); /* Allowed traps. */ priv(rp)->s_trap_mask = (proc_nr == CLOCK || proc_nr == SYSTEM ? CSK_T : TSK_T); ipc_to_m = TSK_M; /* allowed targets */ kcalls = TSK_KC; /* allowed kernel calls */ } /* Privileges for the root system process. */ else { assert(isrootsysn(proc_nr)); priv(rp)->s_flags= RSYS_F; /* privilege flags */ priv(rp)->s_trap_mask= SRV_T; /* allowed traps */ ipc_to_m = SRV_M; /* allowed targets */ kcalls = SRV_KC; /* allowed kernel calls */ priv(rp)->s_sig_mgr = SRV_SM; /* signal manager */ rp->p_priority = SRV_Q; /* priority queue */ rp->p_quantum_size_ms = SRV_QT; /* quantum size */ } /* Fill in target mask. */ memset(&map, 0, sizeof(map)); if (ipc_to_m == ALL_M) { for(j = 0; j < NR_SYS_PROCS; j++) set_sys_bit(map, j); } fill_sendto_mask(rp, &map); /* Fill in kernel call mask. */ for(j = 0; j < SYS_CALL_MASK_SIZE; j++) { priv(rp)->s_k_call_mask[j] = (kcalls == NO_C ? 0 : (~0)); } } else { /* Don't let the process run for now. */ RTS_SET(rp, RTS_NO_PRIV | RTS_NO_QUANTUM); } /* Arch-specific state initialization. */ arch_boot_proc(ip, rp); /* scheduling functions depend on proc_ptr pointing somewhere. */ if(!get_cpulocal_var(proc_ptr)) get_cpulocal_var(proc_ptr) = rp; /* Process isn't scheduled until VM has set up a pagetable for it. */ if(rp->p_nr != VM_PROC_NR && rp->p_nr >= 0) { rp->p_rts_flags |= RTS_VMINHIBIT; rp->p_rts_flags |= RTS_BOOTINHIBIT; } rp->p_rts_flags |= RTS_PROC_STOP; rp->p_rts_flags &= ~RTS_SLOT_FREE; DEBUGEXTRA(("done\n")); } /* update boot procs info for VM */ memcpy(kinfo.boot_procs, image, sizeof(kinfo.boot_procs)); #define IPCNAME(n) { \ assert((n) >= 0 && (n) <= IPCNO_HIGHEST); \ assert(!ipc_call_names[n]); \ ipc_call_names[n] = #n; \ } arch_post_init(); IPCNAME(SEND); IPCNAME(RECEIVE); IPCNAME(SENDREC); IPCNAME(NOTIFY); IPCNAME(SENDNB); IPCNAME(SENDA); /* System and processes initialization */ memory_init(); DEBUGEXTRA(("system_init()... ")); system_init(); DEBUGEXTRA(("done\n")); /* The bootstrap phase is over, so we can add the physical * memory used for it to the free list. */ add_memmap(&kinfo, kinfo.bootstrap_start, kinfo.bootstrap_len); #ifdef CONFIG_SMP if (config_no_apic) { DEBUGBASIC(("APIC disabled, disables SMP, using legacy PIC\n")); smp_single_cpu_fallback(); } else if (config_no_smp) { DEBUGBASIC(("SMP disabled, using legacy PIC\n")); smp_single_cpu_fallback(); } else { smp_init(); /* * if smp_init() returns it means that it failed and we try to finish * single CPU booting */ bsp_finish_booting(); } #else /* * if configured for a single CPU, we are already on the kernel stack which we * are going to use everytime we execute kernel code. We finish booting and we * never return here */ bsp_finish_booting(); #endif NOT_REACHABLE; } /*===========================================================================* * announce * *===========================================================================*/ static void announce(void) { /* Display the MINIX startup banner. */ printf("\nMINIX %s. " #ifdef _VCS_REVISION "(" _VCS_REVISION ")\n" #endif "Copyright 2014, Vrije Universiteit, Amsterdam, The Netherlands\n", OS_RELEASE); printf("MINIX is open source software, see http://www.minix3.org\n"); } /*===========================================================================* * prepare_shutdown * *===========================================================================*/ void prepare_shutdown(const int how) { /* This function prepares to shutdown MINIX. */ static minix_timer_t shutdown_timer; /* Continue after 1 second, to give processes a chance to get scheduled to * do shutdown work. Set a watchog timer to call shutdown(). The timer * argument passes the shutdown status. */ printf("MINIX will now be shut down ...\n"); tmr_arg(&shutdown_timer)->ta_int = how; set_kernel_timer(&shutdown_timer, get_monotonic() + system_hz, minix_shutdown); } /*===========================================================================* * shutdown * *===========================================================================*/ void minix_shutdown(minix_timer_t *tp) { /* This function is called from prepare_shutdown or stop_sequence to bring * down MINIX. */ int how; #ifdef CONFIG_SMP /* * FIXME * * we will need to stop timers on all cpus if SMP is enabled and put them in * such a state that we can perform the whole boot process once restarted from * monitor again */ if (ncpus > 1) smp_shutdown_aps(); #endif hw_intr_disable_all(); stop_local_timer(); how = tp ? tmr_arg(tp)->ta_int : 0; /* Show shutdown message */ direct_cls(); if((how & RB_POWERDOWN) == RB_POWERDOWN) direct_print("MINIX has halted and will now power off.\n"); else if(how & RB_HALT) direct_print("MINIX has halted. " "It is safe to turn off your computer.\n"); else direct_print("MINIX will now reset.\n"); arch_shutdown(how); } /*===========================================================================* * cstart * *===========================================================================*/ void cstart() { /* Perform system initializations prior to calling main(). Most settings are * determined with help of the environment strings passed by MINIX' loader. */ register char *value; /* value in key=value pair */ int h; /* low-level initialization */ prot_init(); /* determine verbosity */ if ((value = env_get(VERBOSEBOOTVARNAME))) verboseboot = atoi(value); /* Get clock tick frequency. */ value = env_get("hz"); if(value) system_hz = atoi(value); if(!value || system_hz < 2 || system_hz > 50000) /* sanity check */ system_hz = DEFAULT_HZ; DEBUGEXTRA(("cstart\n")); /* Record miscellaneous information for user-space servers. */ kinfo.nr_procs = NR_PROCS; kinfo.nr_tasks = NR_TASKS; strlcpy(kinfo.release, OS_RELEASE, sizeof(kinfo.release)); strlcpy(kinfo.version, OS_VERSION, sizeof(kinfo.version)); /* Load average data initialization. */ kloadinfo.proc_last_slot = 0; for(h = 0; h < _LOAD_HISTORY; h++) kloadinfo.proc_load_history[h] = 0; #ifdef USE_APIC value = env_get("no_apic"); if(value) config_no_apic = atoi(value); else config_no_apic = 1; value = env_get("apic_timer_x"); if(value) config_apic_timer_x = atoi(value); else config_apic_timer_x = 1; #endif #ifdef USE_WATCHDOG value = env_get("watchdog"); if (value) watchdog_enabled = atoi(value); #endif #ifdef CONFIG_SMP if (config_no_apic) config_no_smp = 1; value = env_get("no_smp"); if(value) config_no_smp = atoi(value); else config_no_smp = 0; #endif DEBUGEXTRA(("intr_init(0)\n")); intr_init(0); arch_init(); } /*===========================================================================* * get_value * *===========================================================================*/ char *get_value( const char *params, /* boot monitor parameters */ const char *name /* key to look up */ ) { /* Get environment value - kernel version of getenv to avoid setting up the * usual environment array. */ register const char *namep; register char *envp; for (envp = (char *) params; *envp != 0;) { for (namep = name; *namep != 0 && *namep == *envp; namep++, envp++) ; if (*namep == '\0' && *envp == '=') return(envp + 1); while (*envp++ != 0) ; } return(NULL); } /*===========================================================================* * env_get * *===========================================================================*/ char *env_get(const char *name) { return get_value(kinfo.param_buf, name); } void cpu_print_freq(unsigned cpu) { u64_t freq; freq = cpu_get_freq(cpu); DEBUGBASIC(("CPU %d freq %lu MHz\n", cpu, (unsigned long)(freq / 1000000))); } int is_fpu(void) { return get_cpulocal_var(fpu_presence); }