minix/kernel/main.c
Thomas Cort 5142b1f388 kernel: rename realtime to monotonic, add realtime
Old realtime was used for both timers (where an accurate count of
all ticks is needed) and the system time. In order to implement
adjtime(2), these duties must be separated as changing the time
of day by a small amount shouldn't affect timers in any way nor
should it change the boot time.

Following the naming of the clocks used by clock_gettime(2). The
clock named 'realtime' will represent the best guess at the
current wall clock time, and the clock named 'monotonic' will
represent the absolute time the system has been running.
Use monotonic for timers in kernel and in drivers. Use realtime
for determining time of day, dates, etc.

This commit simply renames realtime to monotonic and adds a new
tick counter named realtime. There are no functional changes in
this commit. It just lays the foundation for future work.
2013-04-04 15:04:52 +02:00

511 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/kernel.h"
#include <string.h>
#include <stdlib.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 <minix/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 */
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();
if(NR_BOOT_MODULES != kinfo.mbi.mods_count)
panic("expecting %d boot processes/modules, found %d",
NR_BOOT_MODULES, kinfo.mbi.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 */
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_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) {
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_monotonic() + 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_RESET (hard reset).
*/
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 : RBT_PANIC;
/* Show shutdown message */
direct_cls();
switch(how) {
case RBT_HALT:
direct_print("MINIX has halted. "
"It is safe to turn off your computer.\n");
break;
case RBT_POWEROFF:
direct_print("MINIX has halted and will now power off.\n");
break;
case RBT_DEFAULT:
case RBT_REBOOT:
case RBT_RESET:
default:
direct_print("MINIX will now reset.\n");
break;
}
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);
printf("CPU %d freq %lu MHz\n", cpu, div64u(freq, 1000000));
}
int is_fpu(void)
{
return get_cpulocal_var(fpu_presence);
}