11da177e4SLinus Torvalds /* 2ba180fd4SJeff Dike * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 31da177e4SLinus Torvalds * Licensed under the GPL 41da177e4SLinus Torvalds */ 51da177e4SLinus Torvalds 6c5d4bb17SJeff Dike #include <linux/delay.h> 7c5d4bb17SJeff Dike #include <linux/init.h> 8c5d4bb17SJeff Dike #include <linux/mm.h> 9c5d4bb17SJeff Dike #include <linux/module.h> 10c5d4bb17SJeff Dike #include <linux/seq_file.h> 11c5d4bb17SJeff Dike #include <linux/string.h> 12c5d4bb17SJeff Dike #include <linux/utsname.h> 135b408241SThomas Gleixner #include <linux/sched.h> 1404a41849SThomas Meyer #include <linux/kmsg_dump.h> 15c5d4bb17SJeff Dike #include <asm/pgtable.h> 16c5d4bb17SJeff Dike #include <asm/processor.h> 1733a7d429SGeert Uytterhoeven #include <asm/sections.h> 18c5d4bb17SJeff Dike #include <asm/setup.h> 1937185b33SAl Viro #include <as-layout.h> 2037185b33SAl Viro #include <arch.h> 2137185b33SAl Viro #include <init.h> 2237185b33SAl Viro #include <kern.h> 2337185b33SAl Viro #include <kern_util.h> 2437185b33SAl Viro #include <mem_user.h> 2537185b33SAl Viro #include <os.h> 261da177e4SLinus Torvalds 271da177e4SLinus Torvalds #define DEFAULT_COMMAND_LINE "root=98:0" 281da177e4SLinus Torvalds 291d1497e1SJeff Dike /* Changed in add_arg and setup_arch, which run before SMP is started */ 307a3a06d0SAlon Bar-Lev static char __initdata command_line[COMMAND_LINE_SIZE] = { 0 }; 311da177e4SLinus Torvalds 327a3a06d0SAlon Bar-Lev static void __init add_arg(char *arg) 331da177e4SLinus Torvalds { 341da177e4SLinus Torvalds if (strlen(command_line) + strlen(arg) + 1 > COMMAND_LINE_SIZE) { 351da177e4SLinus Torvalds printf("add_arg: Too many command line arguments!\n"); 361da177e4SLinus Torvalds exit(1); 371da177e4SLinus Torvalds } 381da177e4SLinus Torvalds if (strlen(command_line) > 0) 391da177e4SLinus Torvalds strcat(command_line, " "); 401da177e4SLinus Torvalds strcat(command_line, arg); 411da177e4SLinus Torvalds } 421da177e4SLinus Torvalds 431d1497e1SJeff Dike /* 441d1497e1SJeff Dike * These fields are initialized at boot time and not changed. 451d1497e1SJeff Dike * XXX This structure is used only in the non-SMP case. Maybe this 461d1497e1SJeff Dike * should be moved to smp.c. 471d1497e1SJeff Dike */ 481da177e4SLinus Torvalds struct cpuinfo_um boot_cpu_data = { 491da177e4SLinus Torvalds .loops_per_jiffy = 0, 501da177e4SLinus Torvalds .ipi_pipe = { -1, -1 } 511da177e4SLinus Torvalds }; 521da177e4SLinus Torvalds 535b408241SThomas Gleixner union thread_union cpu0_irqstack 545b408241SThomas Gleixner __attribute__((__section__(".data..init_irqstack"))) = 555b408241SThomas Gleixner { INIT_THREAD_INFO(init_task) }; 565b408241SThomas Gleixner 571da177e4SLinus Torvalds unsigned long thread_saved_pc(struct task_struct *task) 581da177e4SLinus Torvalds { 5977bf4400SJeff Dike /* FIXME: Need to look up userspace_pid by cpu */ 6077bf4400SJeff Dike return os_process_pc(userspace_pid[0]); 611da177e4SLinus Torvalds } 621da177e4SLinus Torvalds 63b4ffb6adSJeff Dike /* Changed in setup_arch, which is called in early boot */ 64b4ffb6adSJeff Dike static char host_info[(__NEW_UTS_LEN + 1) * 5]; 65b4ffb6adSJeff Dike 661da177e4SLinus Torvalds static int show_cpuinfo(struct seq_file *m, void *v) 671da177e4SLinus Torvalds { 681da177e4SLinus Torvalds int index = 0; 691da177e4SLinus Torvalds 701da177e4SLinus Torvalds seq_printf(m, "processor\t: %d\n", index); 711da177e4SLinus Torvalds seq_printf(m, "vendor_id\t: User Mode Linux\n"); 721da177e4SLinus Torvalds seq_printf(m, "model name\t: UML\n"); 736aa802ceSJeff Dike seq_printf(m, "mode\t\t: skas\n"); 741da177e4SLinus Torvalds seq_printf(m, "host\t\t: %s\n", host_info); 751da177e4SLinus Torvalds seq_printf(m, "bogomips\t: %lu.%02lu\n\n", 761da177e4SLinus Torvalds loops_per_jiffy/(500000/HZ), 771da177e4SLinus Torvalds (loops_per_jiffy/(5000/HZ)) % 100); 781da177e4SLinus Torvalds 79a5ed1ffaSJeff Dike return 0; 801da177e4SLinus Torvalds } 811da177e4SLinus Torvalds 821da177e4SLinus Torvalds static void *c_start(struct seq_file *m, loff_t *pos) 831da177e4SLinus Torvalds { 841da177e4SLinus Torvalds return *pos < NR_CPUS ? cpu_data + *pos : NULL; 851da177e4SLinus Torvalds } 861da177e4SLinus Torvalds 871da177e4SLinus Torvalds static void *c_next(struct seq_file *m, void *v, loff_t *pos) 881da177e4SLinus Torvalds { 891da177e4SLinus Torvalds ++*pos; 901da177e4SLinus Torvalds return c_start(m, pos); 911da177e4SLinus Torvalds } 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds static void c_stop(struct seq_file *m, void *v) 941da177e4SLinus Torvalds { 951da177e4SLinus Torvalds } 961da177e4SLinus Torvalds 975e7672ecSJeff Dike const struct seq_operations cpuinfo_op = { 981da177e4SLinus Torvalds .start = c_start, 991da177e4SLinus Torvalds .next = c_next, 1001da177e4SLinus Torvalds .stop = c_stop, 1011da177e4SLinus Torvalds .show = show_cpuinfo, 1021da177e4SLinus Torvalds }; 1031da177e4SLinus Torvalds 1041da177e4SLinus Torvalds /* Set in linux_main */ 1051da177e4SLinus Torvalds unsigned long uml_physmem; 10673395a00SAl Viro EXPORT_SYMBOL(uml_physmem); 10773395a00SAl Viro 1081d1497e1SJeff Dike unsigned long uml_reserved; /* Also modified in mem_init */ 1091da177e4SLinus Torvalds unsigned long start_vm; 1101da177e4SLinus Torvalds unsigned long end_vm; 1111d1497e1SJeff Dike 1121d1497e1SJeff Dike /* Set in uml_ncpus_setup */ 1131da177e4SLinus Torvalds int ncpus = 1; 1141da177e4SLinus Torvalds 1151da177e4SLinus Torvalds /* Set in early boot */ 1161da177e4SLinus Torvalds static int have_root __initdata = 0; 1171d1497e1SJeff Dike 1181d1497e1SJeff Dike /* Set in uml_mem_setup and modified in linux_main */ 119ae173816SJeff Dike long long physmem_size = 32 * 1024 * 1024; 1201da177e4SLinus Torvalds 1213af9c5beSWANG Cong static const char *usage_string = 1221da177e4SLinus Torvalds "User Mode Linux v%s\n" 1231da177e4SLinus Torvalds " available at http://user-mode-linux.sourceforge.net/\n\n"; 1241da177e4SLinus Torvalds 1251da177e4SLinus Torvalds static int __init uml_version_setup(char *line, int *add) 1261da177e4SLinus Torvalds { 12796b644bdSSerge E. Hallyn printf("%s\n", init_utsname()->release); 1281da177e4SLinus Torvalds exit(0); 1291da177e4SLinus Torvalds 1301da177e4SLinus Torvalds return 0; 1311da177e4SLinus Torvalds } 1321da177e4SLinus Torvalds 1331da177e4SLinus Torvalds __uml_setup("--version", uml_version_setup, 1341da177e4SLinus Torvalds "--version\n" 1351da177e4SLinus Torvalds " Prints the version number of the kernel.\n\n" 1361da177e4SLinus Torvalds ); 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds static int __init uml_root_setup(char *line, int *add) 1391da177e4SLinus Torvalds { 1401da177e4SLinus Torvalds have_root = 1; 1411da177e4SLinus Torvalds return 0; 1421da177e4SLinus Torvalds } 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds __uml_setup("root=", uml_root_setup, 1451da177e4SLinus Torvalds "root=<file containing the root fs>\n" 1461da177e4SLinus Torvalds " This is actually used by the generic kernel in exactly the same\n" 1471da177e4SLinus Torvalds " way as in any other kernel. If you configure a number of block\n" 1481da177e4SLinus Torvalds " devices and want to boot off something other than ubd0, you \n" 1491da177e4SLinus Torvalds " would use something like:\n" 1501da177e4SLinus Torvalds " root=/dev/ubd5\n\n" 1511da177e4SLinus Torvalds ); 1521da177e4SLinus Torvalds 153fbd55779SJeff Dike static int __init no_skas_debug_setup(char *line, int *add) 154fbd55779SJeff Dike { 155fbd55779SJeff Dike printf("'debug' is not necessary to gdb UML in skas mode - run \n"); 15696cee304SJeff Dike printf("'gdb linux'\n"); 157fbd55779SJeff Dike 158fbd55779SJeff Dike return 0; 159fbd55779SJeff Dike } 160fbd55779SJeff Dike 161fbd55779SJeff Dike __uml_setup("debug", no_skas_debug_setup, 162fbd55779SJeff Dike "debug\n" 163fbd55779SJeff Dike " this flag is not needed to run gdb on UML in skas mode\n\n" 164fbd55779SJeff Dike ); 165fbd55779SJeff Dike 1661da177e4SLinus Torvalds static int __init Usage(char *line, int *add) 1671da177e4SLinus Torvalds { 1681da177e4SLinus Torvalds const char **p; 1691da177e4SLinus Torvalds 17096b644bdSSerge E. Hallyn printf(usage_string, init_utsname()->release); 1711da177e4SLinus Torvalds p = &__uml_help_start; 1721da177e4SLinus Torvalds while (p < &__uml_help_end) { 1731da177e4SLinus Torvalds printf("%s", *p); 1741da177e4SLinus Torvalds p++; 1751da177e4SLinus Torvalds } 1761da177e4SLinus Torvalds exit(0); 1771da177e4SLinus Torvalds return 0; 1781da177e4SLinus Torvalds } 1791da177e4SLinus Torvalds 1801da177e4SLinus Torvalds __uml_setup("--help", Usage, 1811da177e4SLinus Torvalds "--help\n" 1821da177e4SLinus Torvalds " Prints this message.\n\n" 1831da177e4SLinus Torvalds ); 1841da177e4SLinus Torvalds 1856b7e9674SKarol Swietlicki static void __init uml_checksetup(char *line, int *add) 1861da177e4SLinus Torvalds { 1871da177e4SLinus Torvalds struct uml_param *p; 1881da177e4SLinus Torvalds 1891da177e4SLinus Torvalds p = &__uml_setup_start; 1901da177e4SLinus Torvalds while (p < &__uml_setup_end) { 1913af9c5beSWANG Cong size_t n; 1921da177e4SLinus Torvalds 1931da177e4SLinus Torvalds n = strlen(p->str); 194ba180fd4SJeff Dike if (!strncmp(line, p->str, n) && p->setup_func(line + n, add)) 1956b7e9674SKarol Swietlicki return; 1961da177e4SLinus Torvalds p++; 1971da177e4SLinus Torvalds } 1981da177e4SLinus Torvalds } 1991da177e4SLinus Torvalds 2001da177e4SLinus Torvalds static void __init uml_postsetup(void) 2011da177e4SLinus Torvalds { 2021da177e4SLinus Torvalds initcall_t *p; 2031da177e4SLinus Torvalds 2041da177e4SLinus Torvalds p = &__uml_postsetup_start; 2051da177e4SLinus Torvalds while (p < &__uml_postsetup_end) { 2061da177e4SLinus Torvalds (*p)(); 2071da177e4SLinus Torvalds p++; 2081da177e4SLinus Torvalds } 2091da177e4SLinus Torvalds return; 2101da177e4SLinus Torvalds } 2111da177e4SLinus Torvalds 2120983a88bSJeff Dike static int panic_exit(struct notifier_block *self, unsigned long unused1, 2130983a88bSJeff Dike void *unused2) 2140983a88bSJeff Dike { 21504a41849SThomas Meyer kmsg_dump(KMSG_DUMP_PANIC); 2160983a88bSJeff Dike bust_spinlocks(1); 2170983a88bSJeff Dike bust_spinlocks(0); 2180983a88bSJeff Dike uml_exitcode = 1; 2190983a88bSJeff Dike os_dump_core(); 2200983a88bSJeff Dike return 0; 2210983a88bSJeff Dike } 2220983a88bSJeff Dike 2230983a88bSJeff Dike static struct notifier_block panic_exit_notifier = { 2240983a88bSJeff Dike .notifier_call = panic_exit, 2250983a88bSJeff Dike .next = NULL, 2260983a88bSJeff Dike .priority = 0 2270983a88bSJeff Dike }; 2280983a88bSJeff Dike 2291da177e4SLinus Torvalds /* Set during early boot */ 230536788feSJeff Dike unsigned long task_size; 231536788feSJeff Dike EXPORT_SYMBOL(task_size); 232536788feSJeff Dike 233536788feSJeff Dike unsigned long host_task_size; 234536788feSJeff Dike 2351da177e4SLinus Torvalds unsigned long brk_start; 2361da177e4SLinus Torvalds unsigned long end_iomem; 2371da177e4SLinus Torvalds EXPORT_SYMBOL(end_iomem); 2381da177e4SLinus Torvalds 2391da177e4SLinus Torvalds #define MIN_VMALLOC (32 * 1024 * 1024) 2401da177e4SLinus Torvalds 24123bbd586SJeff Dike extern char __binary_start; 24223bbd586SJeff Dike 2437a3a06d0SAlon Bar-Lev int __init linux_main(int argc, char **argv) 2441da177e4SLinus Torvalds { 2451da177e4SLinus Torvalds unsigned long avail, diff; 2461da177e4SLinus Torvalds unsigned long virtmem_size, max_physmem; 24760a2988aSJeff Dike unsigned long stack; 2483af9c5beSWANG Cong unsigned int i; 2493af9c5beSWANG Cong int add; 2501da177e4SLinus Torvalds 2511da177e4SLinus Torvalds for (i = 1; i < argc; i++) { 252ba180fd4SJeff Dike if ((i == 1) && (argv[i][0] == ' ')) 253ba180fd4SJeff Dike continue; 2541da177e4SLinus Torvalds add = 1; 2551da177e4SLinus Torvalds uml_checksetup(argv[i], &add); 2561da177e4SLinus Torvalds if (add) 2571da177e4SLinus Torvalds add_arg(argv[i]); 2581da177e4SLinus Torvalds } 2591da177e4SLinus Torvalds if (have_root == 0) 2601da177e4SLinus Torvalds add_arg(DEFAULT_COMMAND_LINE); 2611da177e4SLinus Torvalds 26240fb16a3STom Spink host_task_size = os_get_top_address(); 263536788feSJeff Dike /* 264536788feSJeff Dike * TASK_SIZE needs to be PGDIR_SIZE aligned or else exit_mmap craps 265536788feSJeff Dike * out 266536788feSJeff Dike */ 267536788feSJeff Dike task_size = host_task_size & PGDIR_MASK; 268536788feSJeff Dike 269ba180fd4SJeff Dike /* OS sanity checks that need to happen before the kernel runs */ 27060d339f6SGennady Sharapov os_early_checks(); 271cb66504dSPaolo 'Blaisorblade' Giarrusso 2721da177e4SLinus Torvalds brk_start = (unsigned long) sbrk(0); 27377bf4400SJeff Dike 274ba180fd4SJeff Dike /* 275ba180fd4SJeff Dike * Increase physical memory size for exec-shield users 276ba180fd4SJeff Dike * so they actually get what they asked for. This should 277ba180fd4SJeff Dike * add zero for non-exec shield users 278ba180fd4SJeff Dike */ 2791da177e4SLinus Torvalds 2801da177e4SLinus Torvalds diff = UML_ROUND_UP(brk_start) - UML_ROUND_UP(&_end); 2811da177e4SLinus Torvalds if (diff > 1024 * 1024) { 2821da177e4SLinus Torvalds printf("Adding %ld bytes to physical memory to account for " 2831da177e4SLinus Torvalds "exec-shield gap\n", diff); 2841da177e4SLinus Torvalds physmem_size += UML_ROUND_UP(brk_start) - UML_ROUND_UP(&_end); 2851da177e4SLinus Torvalds } 2861da177e4SLinus Torvalds 2871d1497e1SJeff Dike uml_physmem = (unsigned long) &__binary_start & PAGE_MASK; 2881da177e4SLinus Torvalds 2891da177e4SLinus Torvalds /* Reserve up to 4M after the current brk */ 2901da177e4SLinus Torvalds uml_reserved = ROUND_4M(brk_start) + (1 << 22); 2911da177e4SLinus Torvalds 29296b644bdSSerge E. Hallyn setup_machinename(init_utsname()->machine); 2931da177e4SLinus Torvalds 2941da177e4SLinus Torvalds highmem = 0; 2951da177e4SLinus Torvalds iomem_size = (iomem_size + PAGE_SIZE - 1) & PAGE_MASK; 296536788feSJeff Dike max_physmem = TASK_SIZE - uml_physmem - iomem_size - MIN_VMALLOC; 2971da177e4SLinus Torvalds 298ba180fd4SJeff Dike /* 299ba180fd4SJeff Dike * Zones have to begin on a 1 << MAX_ORDER page boundary, 3001da177e4SLinus Torvalds * so this makes sure that's true for highmem 3011da177e4SLinus Torvalds */ 3021da177e4SLinus Torvalds max_physmem &= ~((1 << (PAGE_SHIFT + MAX_ORDER)) - 1); 3031da177e4SLinus Torvalds if (physmem_size + iomem_size > max_physmem) { 3041da177e4SLinus Torvalds highmem = physmem_size + iomem_size - max_physmem; 3051da177e4SLinus Torvalds physmem_size -= highmem; 3061da177e4SLinus Torvalds } 3071da177e4SLinus Torvalds 3081da177e4SLinus Torvalds high_physmem = uml_physmem + physmem_size; 3091da177e4SLinus Torvalds end_iomem = high_physmem + iomem_size; 3101da177e4SLinus Torvalds high_memory = (void *) end_iomem; 3111da177e4SLinus Torvalds 3121da177e4SLinus Torvalds start_vm = VMALLOC_START; 3131da177e4SLinus Torvalds 3141da177e4SLinus Torvalds setup_physmem(uml_physmem, uml_reserved, physmem_size, highmem); 3159e6a57d2SHonggang Li mem_total_pages(physmem_size, iomem_size, highmem); 3161da177e4SLinus Torvalds 3171da177e4SLinus Torvalds virtmem_size = physmem_size; 31860a2988aSJeff Dike stack = (unsigned long) argv; 31960a2988aSJeff Dike stack &= ~(1024 * 1024 - 1); 32060a2988aSJeff Dike avail = stack - start_vm; 321ba180fd4SJeff Dike if (physmem_size > avail) 322ba180fd4SJeff Dike virtmem_size = avail; 3231da177e4SLinus Torvalds end_vm = start_vm + virtmem_size; 3241da177e4SLinus Torvalds 3251da177e4SLinus Torvalds if (virtmem_size < physmem_size) 326ae173816SJeff Dike printf("Kernel virtual memory size shrunk to %lu bytes\n", 3271da177e4SLinus Torvalds virtmem_size); 3281da177e4SLinus Torvalds 3290983a88bSJeff Dike atomic_notifier_chain_register(&panic_notifier_list, 3300983a88bSJeff Dike &panic_exit_notifier); 3310983a88bSJeff Dike 3321da177e4SLinus Torvalds uml_postsetup(); 3331da177e4SLinus Torvalds 33457598fd7SJeff Dike stack_protections((unsigned long) &init_thread_info); 3351da177e4SLinus Torvalds os_flush_stdout(); 3361da177e4SLinus Torvalds 33777bf4400SJeff Dike return start_uml(); 3381da177e4SLinus Torvalds } 3391da177e4SLinus Torvalds 3401da177e4SLinus Torvalds void __init setup_arch(char **cmdline_p) 3411da177e4SLinus Torvalds { 3421da177e4SLinus Torvalds paging_init(); 34319bf7e7aSAlon Bar-Lev strlcpy(boot_command_line, command_line, COMMAND_LINE_SIZE); 3441da177e4SLinus Torvalds *cmdline_p = command_line; 345b4ffb6adSJeff Dike setup_hostinfo(host_info, sizeof host_info); 3461da177e4SLinus Torvalds } 3471da177e4SLinus Torvalds 3481da177e4SLinus Torvalds void __init check_bugs(void) 3491da177e4SLinus Torvalds { 3501da177e4SLinus Torvalds arch_check_bugs(); 3518e367065SJeff Dike os_check_bugs(); 3521da177e4SLinus Torvalds } 3531da177e4SLinus Torvalds 3549a0b5817SGerd Hoffmann void apply_alternatives(struct alt_instr *start, struct alt_instr *end) 3559a0b5817SGerd Hoffmann { 3569a0b5817SGerd Hoffmann } 357