485 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			485 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* 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.h"
 | |
| #include <string.h>
 | |
| #include <stdlib.h>
 | |
| #include <unistd.h>
 | |
| #include <assert.h>
 | |
| #include <libexec.h>
 | |
| #include <a.out.h>
 | |
| #include <minix/com.h>
 | |
| #include <minix/endpoint.h>
 | |
| #include <machine/vmparam.h>
 | |
| #include <minix/u64.h>
 | |
| #include <minix/type.h>
 | |
| #include "proc.h"
 | |
| #include "debug.h"
 | |
| #include "clock.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;
 | |
| 
 | |
|   /* save a global copy of the boot parameters */
 | |
|   memcpy(&kinfo, local_cbi, sizeof(kinfo));
 | |
|   memcpy(&kmess, kinfo.kmess, sizeof(kmess));
 | |
| 
 | |
|   /* We can talk now */
 | |
|   printf("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();
 | |
| 
 | |
|   /* 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 */
 | |
| 	make_zero64(rp->p_cpu_time_left);
 | |
| 	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));
 | |
| 
 | |
|             /* Priviliges 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 */
 | |
|             }
 | |
|             /* Priviliges 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_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) {
 | |
| 	  BOOT_VERBOSE(printf("APIC disabled, disables SMP, using legacy PIC\n"));
 | |
| 	  smp_single_cpu_fallback();
 | |
|   } else if (config_no_smp) {
 | |
| 	  BOOT_VERBOSE(printf("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.%s. "
 | |
| #ifdef _VCS_REVISION
 | |
| 	"(" _VCS_REVISION ")\n"
 | |
| #endif
 | |
|       "Copyright 2012, Vrije Universiteit, Amsterdam, The Netherlands\n",
 | |
|       OS_RELEASE, OS_VERSION);
 | |
|   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 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_timer(&shutdown_timer, get_uptime() + system_hz, minix_shutdown);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				shutdown 				     *
 | |
|  *===========================================================================*/
 | |
| void minix_shutdown(timer_t *tp)
 | |
| {
 | |
| /* This function is called from prepare_shutdown or stop_sequence to bring 
 | |
|  * down MINIX. How to shutdown is in the argument: RBT_HALT (return to the
 | |
|  * monitor), RBT_MONITOR (execute given code), RBT_RESET (hard reset). 
 | |
|  */
 | |
| #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();
 | |
|   arch_shutdown(tp ? tmr_arg(tp)->ta_int : RBT_PANIC);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				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);
 | |
|         printf("CPU %d freq %lu MHz\n", cpu, div64u(freq, 1000000));
 | |
| }
 | |
| 
 | |
| int is_fpu(void)
 | |
| {
 | |
|         return get_cpulocal_var(fpu_presence);
 | |
| }
 | |
| 
 | 
