blob: e9030fcc72a96cff2ae44892b60b7f7ab5659568 [file] [log] [blame]
/* See COPYRIGHT for copyright information. */
#ifdef CONFIG_BSD_ON_CORE0
#error "Yeah, it's not possible to build ROS with BSD on Core 0, sorry......"
#else
#include <arch/arch.h>
#include <arch/topology.h>
#include <arch/console.h>
#include <multiboot.h>
#include <smp.h>
#include <time.h>
#include <atomic.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <monitor.h>
#include <pmap.h>
#include <process.h>
#include <trap.h>
#include <syscall.h>
#include <manager.h>
#include <testing.h>
#include <kmalloc.h>
#include <hashtable.h>
#include <radix.h>
#include <mm.h>
#include <ex_table.h>
#include <percpu.h>
#include <arch/init.h>
#include <bitmask.h>
#include <slab.h>
#include <kthread.h>
#include <linker_func.h>
#include <net/ip.h>
#include <acpi.h>
#include <coreboot_tables.h>
#include <rcu.h>
#define MAX_BOOT_CMDLINE_SIZE 4096
#define ASSIGN_PTRVAL(prm, top, val) \
do { \
if (prm && (prm < top)) { \
*prm = val; \
prm++; \
} \
} while (0)
bool booting = TRUE;
struct proc_global_info __proc_global_info;
struct sysinfo_t sysinfo;
static char boot_cmdline[MAX_BOOT_CMDLINE_SIZE];
static void run_linker_funcs(void);
static int run_init_script(void);
const char *get_boot_option(const char *base, const char *option, char *param,
size_t max_param)
{
size_t optlen = strlen(option);
char *ptop = param + max_param - 1;
const char *opt, *arg;
if (!base)
base = boot_cmdline;
for (;;) {
opt = strstr(base, option);
if (!opt)
return NULL;
if (((opt == base) || (opt[-1] == ' ')) &&
((opt[optlen] == 0) || (opt[optlen] == '=') ||
(opt[optlen] == ' ')))
break;
base = opt + optlen;
}
arg = opt + optlen;
if (*arg == '=') {
arg++;
if (*arg == '\'') {
arg++;
for (; *arg; arg++) {
if (*arg == '\\')
arg++;
else if (*arg == '\'')
break;
ASSIGN_PTRVAL(param, ptop, *arg);
}
} else {
for (; *arg && (*arg != ' '); arg++)
ASSIGN_PTRVAL(param, ptop, *arg);
}
}
ASSIGN_PTRVAL(param, ptop, 0);
return arg;
}
static void extract_multiboot_cmdline(struct multiboot_info *mbi)
{
if (mbi && (mbi->flags & MULTIBOOT_INFO_CMDLINE) && mbi->cmdline) {
const char *cmdln = (const char *) KADDR(mbi->cmdline);
/* We need to copy the command line in a permanent buffer, since
* the multiboot memory where it is currently residing will be
* part of the free boot memory later on in the boot process. */
strlcpy(boot_cmdline, cmdln, sizeof(boot_cmdline));
}
}
static void __kernel_init_part_deux(void *arg);
void kernel_init(multiboot_info_t *mboot_info)
{
extern char __start_bss[], __stop_bss[];
memset(__start_bss, 0, __stop_bss - __start_bss);
/* mboot_info is a physical address. while some arches currently have
* the lower memory mapped, everyone should have it mapped at kernbase
* by now. also, it might be in 'free' memory, so once we start
* dynamically using memory, we may clobber it. */
multiboot_kaddr = (struct multiboot_info*)((physaddr_t)mboot_info
+ KERNBASE);
extract_multiboot_cmdline(multiboot_kaddr);
cons_init();
print_cpuinfo();
printk("Boot Command Line: '%s'\n", boot_cmdline);
exception_table_init();
num_cores = get_early_num_cores();
pmem_init(multiboot_kaddr);
kmalloc_init();
vmap_init();
hashtable_init();
radix_init();
acpiinit();
topology_init();
percpu_init();
kthread_init(); /* might need to tweak when this happens */
vmr_init();
page_check();
idt_init();
/* After kthread_init and idt_init, we can use a real kstack. */
__use_real_kstack(__kernel_init_part_deux);
}
static void __kernel_init_part_deux(void *arg)
{
kernel_msg_init();
timer_init();
time_init();
arch_init();
rcu_init();
enable_irq();
run_linker_funcs();
/* reset/init devtab after linker funcs 3 and 4. these run NIC and
* medium pre-inits, which need to happen before devether. */
devtabreset();
devtabinit();
#ifdef CONFIG_ETH_AUDIO
eth_audio_init();
#endif /* CONFIG_ETH_AUDIO */
get_coreboot_info(&sysinfo);
booting = FALSE;
#ifdef CONFIG_RUN_INIT_SCRIPT
if (run_init_script()) {
printk("Told to run init script, but no script specified\n");
manager();
}
#else
manager();
#endif
}
#ifdef CONFIG_RUN_INIT_SCRIPT
static int run_init_script(void)
{
/* If we have an init script path specified */
if (strlen(CONFIG_INIT_SCRIPT_PATH_AND_ARGS) != 0) {
int vargs = 0;
char *sptr = &CONFIG_INIT_SCRIPT_PATH_AND_ARGS[0];
/* Figure out how many arguments there are, by finding the
* spaces */
/* TODO: consider rewriting this stuff with parsecmd */
while (*sptr != '\0') {
if (*(sptr++) != ' ') {
vargs++;
while ((*sptr != ' ') && (*sptr != '\0'))
sptr++;
}
}
/* Initialize l_argv with its first three arguments, but
* allocate space for all arguments as calculated above */
int static_args = 2;
int total_args = vargs + static_args;
char *l_argv[total_args];
l_argv[0] = "/bin/bash";
l_argv[1] = "bash";
/* Initialize l_argv with the rest of the arguments */
int i = static_args;
sptr = &CONFIG_INIT_SCRIPT_PATH_AND_ARGS[0];
while (*sptr != '\0') {
if (*sptr != ' ') {
l_argv[i++] = sptr;
while ((*sptr != ' ') && (*sptr != '\0'))
sptr++;
if (*sptr == '\0')
break;
*sptr = '\0';
}
sptr++;
}
/* Run the script with its arguments */
mon_bin_run(total_args, l_argv, NULL);
}
return -1;
}
#endif
/* Multiple cores can panic concurrently. We could also panic recursively,
* which could deadlock. We also only want to automatically backtrace the first
* time through, since BTs are often the source of panics. Finally, we want to
* know when the other panicking cores are done (or likely to be done) before
* entering the monitor.
*
* We'll use the print_lock(), which is recursive, to protect panic_printing. */
static bool panic_printing;
static DEFINE_PERCPU(int, panic_depth);
/*
* Panic is called on unresolvable fatal errors.
* It prints "panic: mesg", and then enters the kernel monitor.
*/
void _panic(struct hw_trapframe *hw_tf, const char *file, int line,
const char *fmt, ...)
{
struct per_cpu_info *pcpui = &per_cpu_info[core_id_early()];
va_list ap;
print_lock();
panic_printing = true;
PERCPU_VAR(panic_depth)++;
va_start(ap, fmt);
printk("\nkernel panic at %s:%d, from core %d: ", file, line,
core_id_early());
vcprintf(fmt, ap);
printk("\n");
va_end(ap);
/* Recursive panics are usually backtrace problems. Possibly printk.
* Locking panics might recurse forever. */
if (PERCPU_VAR(panic_depth) == 1) {
if (hw_tf) {
print_trapframe(hw_tf);
backtrace_hwtf(hw_tf);
} else {
backtrace();
}
} else {
printk("\tRecursive kernel panic on core %d (depth %d)\n",
core_id_early(), PERCPU_VAR(panic_depth));
}
printk("\n");
/* If we're here, we panicked and currently hold the print_lock. We
* might have panicked recursively. We must unlock unconditionally,
* since the initial panic (which grabbed the lock) will never run
* again. */
panic_printing = false;
print_unlock_force();
/* And we have to clear the depth, so that we lock again next time in.
* Otherwise, we'd be unlocking without locking (which is another
* panic). */
PERCPU_VAR(panic_depth) = 0;
/* Let's wait long enough for other printers to finish before entering
* the monitor. */
do {
udelay(500000);
cmb();
} while (panic_printing);
/* Yikes! We're claiming to be not in IRQ/trap ctx and not holding any
* locks. Obviously we could be wrong, and could easily deadlock. We
* could be in an IRQ handler, an unhandled kernel fault, or just a
* 'normal' panic in a syscall - any of which can involve unrestore
* invariants. */
pcpui->__ctx_depth = 0;
pcpui->lock_depth = 0;
/* And keep this off, for good measure. */
pcpui->__lock_checking_enabled--;
monitor(NULL);
if (pcpui->cur_proc) {
printk("panic killing proc %d\n", pcpui->cur_proc->pid);
proc_destroy(pcpui->cur_proc);
}
if (pcpui->cur_kthread)
kth_panic_sysc(pcpui->cur_kthread);
smp_idle();
}
void _warn(const char *file, int line, const char *fmt,...)
{
va_list ap;
print_lock();
va_start(ap, fmt);
printk("\nkernel warning at %s:%d, from core %d: ", file, line,
core_id_early());
vcprintf(fmt, ap);
printk("\n");
va_end(ap);
backtrace();
printk("\n");
print_unlock();
}
static void run_links(linker_func_t *linkstart, linker_func_t *linkend)
{
/* Unlike with devtab, our linker sections for the function pointers are
* 8 byte aligned (4 on 32 bit) (done by the linker/compiler), so we
* don't have to worry about that. */
printd("linkstart %p, linkend %p\n", linkstart, linkend);
for (int i = 0; &linkstart[i] < linkend; i++) {
printd("i %d, linkfunc %p\n", i, linkstart[i]);
linkstart[i]();
}
}
static void run_linker_funcs(void)
{
run_links(__linkerfunc1start, __linkerfunc1end);
run_links(__linkerfunc2start, __linkerfunc2end);
run_links(__linkerfunc3start, __linkerfunc3end);
run_links(__linkerfunc4start, __linkerfunc4end);
}
/* You need to reference PROVIDE symbols somewhere, or they won't be included.
* Only really a problem for debugging. */
void debug_linker_tables(void)
{
extern struct dev __devtabstart[];
extern struct dev __devtabend[];
printk("devtab %p %p\nlink1 %p %p\nlink2 %p %p\nlink3 %p %p\nlink4 %p %p\n",
__devtabstart,
__devtabend,
__linkerfunc1start,
__linkerfunc1end,
__linkerfunc2start,
__linkerfunc2end,
__linkerfunc3start,
__linkerfunc3end,
__linkerfunc4start,
__linkerfunc4end);
}
#endif //Everything For Free