minix/minix/kernel/main.c
David van Moolenbroek d91f738bd8 Kernel: export clock information on kernel page
Please note that this information is for use by system services only!
The clock facility is not ready to be used directly by userland, and
thus, this kernel page extension is NOT part of the userland ABI.

For service programmers' convenience, change the prototype of the
getticks(3) to return the uptime clock value directly, since the call
can no longer fail.

Correct the sys_times(2) reply message to use the right field type
for the boot time.

Restructure the kernel internals a bit so as to have all the clock
stuff closer together.

Change-Id: Ifc050b7bd253aecbe46e3bd7d7cc75bd86e45555
2015-09-23 12:00:46 +00:00

520 lines
15 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/kernel.h"
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <minix/com.h>
#include <minix/endpoint.h>
#include <machine/vmparam.h>
#include <minix/u64.h>
#include <minix/board.h>
#include <minix/type.h>
#include <sys/reboot.h>
#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 */
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"));
/* Clear the process table. Anounce each slot as empty and set up mappings
* for proc_addr() and proc_nr() macros. Do the same for the table with
* privilege structures for the system processes and the ipc filter pool.
*/
proc_init();
IPCF_POOL_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);
/* Init flags. */
priv(rp)->s_init_flags = TSK_I;
/* 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_init_flags = SRV_I; /* init 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 */
/* low-level initialization */
prot_init();
/* determine verbosity */
if ((value = env_get(VERBOSEBOOTVARNAME)))
verboseboot = atoi(value);
/* Initialize clock variables. */
init_clock();
/* Get memory parameters. */
value = env_get("ac_layout");
if(value && atoi(value)) {
kinfo.user_sp = (vir_bytes) USR_STACKTOP_COMPACT;
kinfo.user_end = (vir_bytes) USR_DATATOP_COMPACT;
}
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));
#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);
}