a06e2ab395
import/switch of: init, getty, reboot, halt, shutdown, wall, last changes: . change reboot() call to netbsd prototype and args . allows pristine <utmp.h> . use clean <sys/reboot.h> instead of <minix/reboot.h> . implement TIOCSCTTY for use by getty so getty can get controlling terminal from init's child(ren) . allow NULL envp for exec Change-Id: I5ca02cb4230857140c08794bbfeba7df982c58a3
510 lines
14 KiB
C
510 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 <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 */
|
|
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 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 */
|
|
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 */
|
|
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) {
|
|
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. "
|
|
#ifdef _VCS_REVISION
|
|
"(" _VCS_REVISION ")\n"
|
|
#endif
|
|
"Copyright 2012, 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);
|
|
printf("CPU %d freq %lu MHz\n", cpu, div64u(freq, 1000000));
|
|
}
|
|
|
|
int is_fpu(void)
|
|
{
|
|
return get_cpulocal_var(fpu_presence);
|
|
}
|
|
|