50e2064049
This commit removes all traces of Minix segments (the text/data/stack memory map abstraction in the kernel) and significance of Intel segments (hardware segments like CS, DS that add offsets to all addressing before page table translation). This ultimately simplifies the memory layout and addressing and makes the same layout possible on non-Intel architectures. There are only two types of addresses in the world now: virtual and physical; even the kernel and processes have the same virtual address space. Kernel and user processes can be distinguished at a glance as processes won't use 0xF0000000 and above. No static pre-allocated memory sizes exist any more. Changes to booting: . The pre_init.c leaves the kernel and modules exactly as they were left by the bootloader in physical memory . The kernel starts running using physical addressing, loaded at a fixed location given in its linker script by the bootloader. All code and data in this phase are linked to this fixed low location. . It makes a bootstrap pagetable to map itself to a fixed high location (also in linker script) and jumps to the high address. All code and data then use this high addressing. . All code/data symbols linked at the low addresses is prefixed by an objcopy step with __k_unpaged_*, so that that code cannot reference highly-linked symbols (which aren't valid yet) or vice versa (symbols that aren't valid any more). . The two addressing modes are separated in the linker script by collecting the unpaged_*.o objects and linking them with low addresses, and linking the rest high. Some objects are linked twice, once low and once high. . The bootstrap phase passes a lot of information (e.g. free memory list, physical location of the modules, etc.) using the kinfo struct. . After this bootstrap the low-linked part is freed. . The kernel maps in VM into the bootstrap page table so that VM can begin executing. Its first job is to make page tables for all other boot processes. So VM runs before RS, and RS gets a fully dynamic, VM-managed address space. VM gets its privilege info from RS as usual but that happens after RS starts running. . Both the kernel loading VM and VM organizing boot processes happen using the libexec logic. This removes the last reason for VM to still know much about exec() and vm/exec.c is gone. Further Implementation: . All segments are based at 0 and have a 4 GB limit. . The kernel is mapped in at the top of the virtual address space so as not to constrain the user processes. . Processes do not use segments from the LDT at all; there are no segments in the LDT any more, so no LLDT is needed. . The Minix segments T/D/S are gone and so none of the user-space or in-kernel copy functions use them. The copy functions use a process endpoint of NONE to realize it's a physical address, virtual otherwise. . The umap call only makes sense to translate a virtual address to a physical address now. . Segments-related calls like newmap and alloc_segments are gone. . All segments-related translation in VM is gone (vir2map etc). . Initialization in VM is simpler as no moving around is necessary. . VM and all other boot processes can be linked wherever they wish and will be mapped in at the right location by the kernel and VM respectively. Other changes: . The multiboot code is less special: it does not use mb_print for its diagnostics any more but uses printf() as normal, saving the output into the diagnostics buffer, only printing to the screen using the direct print functions if a panic() occurs. . The multiboot code uses the flexible 'free memory map list' style to receive the list of free memory if available. . The kernel determines the memory layout of the processes to a degree: it tells VM where the kernel starts and ends and where the kernel wants the top of the process to be. VM then uses this entire range, i.e. the stack is right at the top, and mmap()ped bits of memory are placed below that downwards, and the break grows upwards. Other Consequences: . Every process gets its own page table as address spaces can't be separated any more by segments. . As all segments are 0-based, there is no distinction between virtual and linear addresses, nor between userspace and kernel addresses. . Less work is done when context switching, leading to a net performance increase. (8% faster on my machine for 'make servers'.) . The layout and configuration of the GDT makes sysenter and syscall possible.
481 lines
14 KiB
C
481 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
|
|
|
|
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");
|
|
|
|
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) {
|
|
/* 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 if(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 */
|
|
}
|
|
/* Priviliges for ordinary process. */
|
|
else {
|
|
NOT_REACHABLE;
|
|
}
|
|
|
|
/* 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;
|
|
strncpy(kinfo.release, OS_RELEASE, sizeof(kinfo.release));
|
|
kinfo.release[sizeof(kinfo.release)-1] = '\0';
|
|
strncpy(kinfo.version, OS_VERSION, sizeof(kinfo.version));
|
|
kinfo.version[sizeof(kinfo.version)-1] = '\0';
|
|
|
|
/* 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);
|
|
}
|
|
|