cf9a4ec79b
Coverity was flagging a recursive include between kernel.h and cpulocals.h. As cpulocals.h also included proc.h, we can move that include statement into kernel.h, and clean up the source files' include statements accordingly.
482 lines
14 KiB
C
482 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 "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);
|
|
}
|
|
|