10d1fb0a4SAlex Dewar // SPDX-License-Identifier: GPL-2.0 21da177e4SLinus Torvalds /* 3ba180fd4SJeff Dike * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 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> 149164bb4aSIngo Molnar #include <linux/sched/task.h> 1504a41849SThomas Meyer #include <linux/kmsg_dump.h> 1692dcd3d3SJohannes Berg #include <linux/suspend.h> 179164bb4aSIngo Molnar 18c5d4bb17SJeff Dike #include <asm/processor.h> 1933a7d429SGeert Uytterhoeven #include <asm/sections.h> 20c5d4bb17SJeff Dike #include <asm/setup.h> 2137185b33SAl Viro #include <as-layout.h> 2237185b33SAl Viro #include <arch.h> 2337185b33SAl Viro #include <init.h> 2437185b33SAl Viro #include <kern.h> 2537185b33SAl Viro #include <kern_util.h> 2637185b33SAl Viro #include <mem_user.h> 2737185b33SAl Viro #include <os.h> 281da177e4SLinus Torvalds 29d7ffac33SThomas Meyer #define DEFAULT_COMMAND_LINE_ROOT "root=98:0" 30d7ffac33SThomas Meyer #define DEFAULT_COMMAND_LINE_CONSOLE "console=tty" 311da177e4SLinus Torvalds 321d1497e1SJeff Dike /* Changed in add_arg and setup_arch, which run before SMP is started */ 337a3a06d0SAlon Bar-Lev static char __initdata command_line[COMMAND_LINE_SIZE] = { 0 }; 341da177e4SLinus Torvalds 357a3a06d0SAlon Bar-Lev static void __init add_arg(char *arg) 361da177e4SLinus Torvalds { 371da177e4SLinus Torvalds if (strlen(command_line) + strlen(arg) + 1 > COMMAND_LINE_SIZE) { 380936d4f3SMasami Hiramatsu os_warn("add_arg: Too many command line arguments!\n"); 391da177e4SLinus Torvalds exit(1); 401da177e4SLinus Torvalds } 411da177e4SLinus Torvalds if (strlen(command_line) > 0) 421da177e4SLinus Torvalds strcat(command_line, " "); 431da177e4SLinus Torvalds strcat(command_line, arg); 441da177e4SLinus Torvalds } 451da177e4SLinus Torvalds 461d1497e1SJeff Dike /* 471d1497e1SJeff Dike * These fields are initialized at boot time and not changed. 481d1497e1SJeff Dike * XXX This structure is used only in the non-SMP case. Maybe this 491d1497e1SJeff Dike * should be moved to smp.c. 501d1497e1SJeff Dike */ 511da177e4SLinus Torvalds struct cpuinfo_um boot_cpu_data = { 521da177e4SLinus Torvalds .loops_per_jiffy = 0, 531da177e4SLinus Torvalds .ipi_pipe = { -1, -1 } 541da177e4SLinus Torvalds }; 551da177e4SLinus Torvalds 565b408241SThomas Gleixner union thread_union cpu0_irqstack 5733def849SJoe Perches __section(".data..init_irqstack") = 580500871fSDavid Howells { .thread_info = INIT_THREAD_INFO(init_task) }; 595b408241SThomas Gleixner 60b4ffb6adSJeff Dike /* Changed in setup_arch, which is called in early boot */ 61b4ffb6adSJeff Dike static char host_info[(__NEW_UTS_LEN + 1) * 5]; 62b4ffb6adSJeff Dike 631da177e4SLinus Torvalds static int show_cpuinfo(struct seq_file *m, void *v) 641da177e4SLinus Torvalds { 651da177e4SLinus Torvalds int index = 0; 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds seq_printf(m, "processor\t: %d\n", index); 681da177e4SLinus Torvalds seq_printf(m, "vendor_id\t: User Mode Linux\n"); 691da177e4SLinus Torvalds seq_printf(m, "model name\t: UML\n"); 706aa802ceSJeff Dike seq_printf(m, "mode\t\t: skas\n"); 711da177e4SLinus Torvalds seq_printf(m, "host\t\t: %s\n", host_info); 721da177e4SLinus Torvalds seq_printf(m, "bogomips\t: %lu.%02lu\n\n", 731da177e4SLinus Torvalds loops_per_jiffy/(500000/HZ), 741da177e4SLinus Torvalds (loops_per_jiffy/(5000/HZ)) % 100); 751da177e4SLinus Torvalds 76a5ed1ffaSJeff Dike return 0; 771da177e4SLinus Torvalds } 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds static void *c_start(struct seq_file *m, loff_t *pos) 801da177e4SLinus Torvalds { 811da177e4SLinus Torvalds return *pos < NR_CPUS ? cpu_data + *pos : NULL; 821da177e4SLinus Torvalds } 831da177e4SLinus Torvalds 841da177e4SLinus Torvalds static void *c_next(struct seq_file *m, void *v, loff_t *pos) 851da177e4SLinus Torvalds { 861da177e4SLinus Torvalds ++*pos; 871da177e4SLinus Torvalds return c_start(m, pos); 881da177e4SLinus Torvalds } 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds static void c_stop(struct seq_file *m, void *v) 911da177e4SLinus Torvalds { 921da177e4SLinus Torvalds } 931da177e4SLinus Torvalds 945e7672ecSJeff Dike const struct seq_operations cpuinfo_op = { 951da177e4SLinus Torvalds .start = c_start, 961da177e4SLinus Torvalds .next = c_next, 971da177e4SLinus Torvalds .stop = c_stop, 981da177e4SLinus Torvalds .show = show_cpuinfo, 991da177e4SLinus Torvalds }; 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds /* Set in linux_main */ 1021da177e4SLinus Torvalds unsigned long uml_physmem; 10373395a00SAl Viro EXPORT_SYMBOL(uml_physmem); 10473395a00SAl Viro 1051d1497e1SJeff Dike unsigned long uml_reserved; /* Also modified in mem_init */ 1061da177e4SLinus Torvalds unsigned long start_vm; 1071da177e4SLinus Torvalds unsigned long end_vm; 1081d1497e1SJeff Dike 1091d1497e1SJeff Dike /* Set in uml_ncpus_setup */ 1101da177e4SLinus Torvalds int ncpus = 1; 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds /* Set in early boot */ 113d7ffac33SThomas Meyer static int have_root __initdata; 114d7ffac33SThomas Meyer static int have_console __initdata; 1151d1497e1SJeff Dike 1161d1497e1SJeff Dike /* Set in uml_mem_setup and modified in linux_main */ 117ae173816SJeff Dike long long physmem_size = 32 * 1024 * 1024; 1185d38f324SErel Geron EXPORT_SYMBOL(physmem_size); 1191da177e4SLinus Torvalds 1203af9c5beSWANG Cong static const char *usage_string = 1211da177e4SLinus Torvalds "User Mode Linux v%s\n" 1221da177e4SLinus Torvalds " available at http://user-mode-linux.sourceforge.net/\n\n"; 1231da177e4SLinus Torvalds 1241da177e4SLinus Torvalds static int __init uml_version_setup(char *line, int *add) 1251da177e4SLinus Torvalds { 1260936d4f3SMasami Hiramatsu /* Explicitly use printf() to show version in stdout */ 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 { 1550936d4f3SMasami Hiramatsu os_warn("'debug' is not necessary to gdb UML in skas mode - run\n"); 1560936d4f3SMasami Hiramatsu os_warn("'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 166d7ffac33SThomas Meyer static int __init uml_console_setup(char *line, int *add) 167d7ffac33SThomas Meyer { 168d7ffac33SThomas Meyer have_console = 1; 169d7ffac33SThomas Meyer return 0; 170d7ffac33SThomas Meyer } 171d7ffac33SThomas Meyer 172d7ffac33SThomas Meyer __uml_setup("console=", uml_console_setup, 173d7ffac33SThomas Meyer "console=<preferred console>\n" 174d7ffac33SThomas Meyer " Specify the preferred console output driver\n\n" 175d7ffac33SThomas Meyer ); 176d7ffac33SThomas Meyer 1771da177e4SLinus Torvalds static int __init Usage(char *line, int *add) 1781da177e4SLinus Torvalds { 1791da177e4SLinus Torvalds const char **p; 1801da177e4SLinus Torvalds 18196b644bdSSerge E. Hallyn printf(usage_string, init_utsname()->release); 1821da177e4SLinus Torvalds p = &__uml_help_start; 1830936d4f3SMasami Hiramatsu /* Explicitly use printf() to show help in stdout */ 1841da177e4SLinus Torvalds while (p < &__uml_help_end) { 1851da177e4SLinus Torvalds printf("%s", *p); 1861da177e4SLinus Torvalds p++; 1871da177e4SLinus Torvalds } 1881da177e4SLinus Torvalds exit(0); 1891da177e4SLinus Torvalds return 0; 1901da177e4SLinus Torvalds } 1911da177e4SLinus Torvalds 1921da177e4SLinus Torvalds __uml_setup("--help", Usage, 1931da177e4SLinus Torvalds "--help\n" 1941da177e4SLinus Torvalds " Prints this message.\n\n" 1951da177e4SLinus Torvalds ); 1961da177e4SLinus Torvalds 1976b7e9674SKarol Swietlicki static void __init uml_checksetup(char *line, int *add) 1981da177e4SLinus Torvalds { 1991da177e4SLinus Torvalds struct uml_param *p; 2001da177e4SLinus Torvalds 2011da177e4SLinus Torvalds p = &__uml_setup_start; 2021da177e4SLinus Torvalds while (p < &__uml_setup_end) { 2033af9c5beSWANG Cong size_t n; 2041da177e4SLinus Torvalds 2051da177e4SLinus Torvalds n = strlen(p->str); 206ba180fd4SJeff Dike if (!strncmp(line, p->str, n) && p->setup_func(line + n, add)) 2076b7e9674SKarol Swietlicki return; 2081da177e4SLinus Torvalds p++; 2091da177e4SLinus Torvalds } 2101da177e4SLinus Torvalds } 2111da177e4SLinus Torvalds 2121da177e4SLinus Torvalds static void __init uml_postsetup(void) 2131da177e4SLinus Torvalds { 2141da177e4SLinus Torvalds initcall_t *p; 2151da177e4SLinus Torvalds 2161da177e4SLinus Torvalds p = &__uml_postsetup_start; 2171da177e4SLinus Torvalds while (p < &__uml_postsetup_end) { 2181da177e4SLinus Torvalds (*p)(); 2191da177e4SLinus Torvalds p++; 2201da177e4SLinus Torvalds } 2211da177e4SLinus Torvalds return; 2221da177e4SLinus Torvalds } 2231da177e4SLinus Torvalds 2240983a88bSJeff Dike static int panic_exit(struct notifier_block *self, unsigned long unused1, 2250983a88bSJeff Dike void *unused2) 2260983a88bSJeff Dike { 22704a41849SThomas Meyer kmsg_dump(KMSG_DUMP_PANIC); 2280983a88bSJeff Dike bust_spinlocks(1); 2290983a88bSJeff Dike bust_spinlocks(0); 2300983a88bSJeff Dike uml_exitcode = 1; 2310983a88bSJeff Dike os_dump_core(); 2320983a88bSJeff Dike return 0; 2330983a88bSJeff Dike } 2340983a88bSJeff Dike 2350983a88bSJeff Dike static struct notifier_block panic_exit_notifier = { 2360983a88bSJeff Dike .notifier_call = panic_exit, 2370983a88bSJeff Dike .next = NULL, 2380983a88bSJeff Dike .priority = 0 2390983a88bSJeff Dike }; 2400983a88bSJeff Dike 24133bbc306SThomas Meyer void uml_finishsetup(void) 24233bbc306SThomas Meyer { 24333bbc306SThomas Meyer atomic_notifier_chain_register(&panic_notifier_list, 24433bbc306SThomas Meyer &panic_exit_notifier); 24533bbc306SThomas Meyer 24633bbc306SThomas Meyer uml_postsetup(); 24733bbc306SThomas Meyer 24833bbc306SThomas Meyer new_thread_handler(); 24933bbc306SThomas Meyer } 25033bbc306SThomas Meyer 2511da177e4SLinus Torvalds /* Set during early boot */ 252*bfc58e2bSJohannes Berg unsigned long stub_start; 253536788feSJeff Dike unsigned long task_size; 254536788feSJeff Dike EXPORT_SYMBOL(task_size); 255536788feSJeff Dike 256536788feSJeff Dike unsigned long host_task_size; 257536788feSJeff Dike 2581da177e4SLinus Torvalds unsigned long brk_start; 2591da177e4SLinus Torvalds unsigned long end_iomem; 2601da177e4SLinus Torvalds EXPORT_SYMBOL(end_iomem); 2611da177e4SLinus Torvalds 2621da177e4SLinus Torvalds #define MIN_VMALLOC (32 * 1024 * 1024) 2631da177e4SLinus Torvalds 2647a3a06d0SAlon Bar-Lev int __init linux_main(int argc, char **argv) 2651da177e4SLinus Torvalds { 2661da177e4SLinus Torvalds unsigned long avail, diff; 2671da177e4SLinus Torvalds unsigned long virtmem_size, max_physmem; 26860a2988aSJeff Dike unsigned long stack; 2693af9c5beSWANG Cong unsigned int i; 2703af9c5beSWANG Cong int add; 2711da177e4SLinus Torvalds 2721da177e4SLinus Torvalds for (i = 1; i < argc; i++) { 273ba180fd4SJeff Dike if ((i == 1) && (argv[i][0] == ' ')) 274ba180fd4SJeff Dike continue; 2751da177e4SLinus Torvalds add = 1; 2761da177e4SLinus Torvalds uml_checksetup(argv[i], &add); 2771da177e4SLinus Torvalds if (add) 2781da177e4SLinus Torvalds add_arg(argv[i]); 2791da177e4SLinus Torvalds } 2801da177e4SLinus Torvalds if (have_root == 0) 281d7ffac33SThomas Meyer add_arg(DEFAULT_COMMAND_LINE_ROOT); 282d7ffac33SThomas Meyer 283d7ffac33SThomas Meyer if (have_console == 0) 284d7ffac33SThomas Meyer add_arg(DEFAULT_COMMAND_LINE_CONSOLE); 2851da177e4SLinus Torvalds 28640fb16a3STom Spink host_task_size = os_get_top_address(); 287*bfc58e2bSJohannes Berg /* reserve two pages for the stubs */ 288*bfc58e2bSJohannes Berg host_task_size -= 2 * PAGE_SIZE; 289*bfc58e2bSJohannes Berg stub_start = host_task_size; 290*bfc58e2bSJohannes Berg 291536788feSJeff Dike /* 292536788feSJeff Dike * TASK_SIZE needs to be PGDIR_SIZE aligned or else exit_mmap craps 293536788feSJeff Dike * out 294536788feSJeff Dike */ 295536788feSJeff Dike task_size = host_task_size & PGDIR_MASK; 296536788feSJeff Dike 297ba180fd4SJeff Dike /* OS sanity checks that need to happen before the kernel runs */ 29860d339f6SGennady Sharapov os_early_checks(); 299cb66504dSPaolo 'Blaisorblade' Giarrusso 3001da177e4SLinus Torvalds brk_start = (unsigned long) sbrk(0); 30177bf4400SJeff Dike 302ba180fd4SJeff Dike /* 303ba180fd4SJeff Dike * Increase physical memory size for exec-shield users 304ba180fd4SJeff Dike * so they actually get what they asked for. This should 305ba180fd4SJeff Dike * add zero for non-exec shield users 306ba180fd4SJeff Dike */ 3071da177e4SLinus Torvalds 3081da177e4SLinus Torvalds diff = UML_ROUND_UP(brk_start) - UML_ROUND_UP(&_end); 3091da177e4SLinus Torvalds if (diff > 1024 * 1024) { 310d3878bb8SMasami Hiramatsu os_info("Adding %ld bytes to physical memory to account for " 3111da177e4SLinus Torvalds "exec-shield gap\n", diff); 3121da177e4SLinus Torvalds physmem_size += UML_ROUND_UP(brk_start) - UML_ROUND_UP(&_end); 3131da177e4SLinus Torvalds } 3141da177e4SLinus Torvalds 31505eacfd0SNicolas Iooss uml_physmem = (unsigned long) __binary_start & PAGE_MASK; 3161da177e4SLinus Torvalds 3171da177e4SLinus Torvalds /* Reserve up to 4M after the current brk */ 3181da177e4SLinus Torvalds uml_reserved = ROUND_4M(brk_start) + (1 << 22); 3191da177e4SLinus Torvalds 32096b644bdSSerge E. Hallyn setup_machinename(init_utsname()->machine); 3211da177e4SLinus Torvalds 3221da177e4SLinus Torvalds highmem = 0; 3231da177e4SLinus Torvalds iomem_size = (iomem_size + PAGE_SIZE - 1) & PAGE_MASK; 324536788feSJeff Dike max_physmem = TASK_SIZE - uml_physmem - iomem_size - MIN_VMALLOC; 3251da177e4SLinus Torvalds 326ba180fd4SJeff Dike /* 327ba180fd4SJeff Dike * Zones have to begin on a 1 << MAX_ORDER page boundary, 3281da177e4SLinus Torvalds * so this makes sure that's true for highmem 3291da177e4SLinus Torvalds */ 3301da177e4SLinus Torvalds max_physmem &= ~((1 << (PAGE_SHIFT + MAX_ORDER)) - 1); 3311da177e4SLinus Torvalds if (physmem_size + iomem_size > max_physmem) { 3321da177e4SLinus Torvalds highmem = physmem_size + iomem_size - max_physmem; 3331da177e4SLinus Torvalds physmem_size -= highmem; 3341da177e4SLinus Torvalds } 3351da177e4SLinus Torvalds 3361da177e4SLinus Torvalds high_physmem = uml_physmem + physmem_size; 3371da177e4SLinus Torvalds end_iomem = high_physmem + iomem_size; 3381da177e4SLinus Torvalds high_memory = (void *) end_iomem; 3391da177e4SLinus Torvalds 3401da177e4SLinus Torvalds start_vm = VMALLOC_START; 3411da177e4SLinus Torvalds 3421da177e4SLinus Torvalds virtmem_size = physmem_size; 34360a2988aSJeff Dike stack = (unsigned long) argv; 34460a2988aSJeff Dike stack &= ~(1024 * 1024 - 1); 34560a2988aSJeff Dike avail = stack - start_vm; 346ba180fd4SJeff Dike if (physmem_size > avail) 347ba180fd4SJeff Dike virtmem_size = avail; 3481da177e4SLinus Torvalds end_vm = start_vm + virtmem_size; 3491da177e4SLinus Torvalds 3501da177e4SLinus Torvalds if (virtmem_size < physmem_size) 351d3878bb8SMasami Hiramatsu os_info("Kernel virtual memory size shrunk to %lu bytes\n", 3521da177e4SLinus Torvalds virtmem_size); 3531da177e4SLinus Torvalds 3541da177e4SLinus Torvalds os_flush_stdout(); 3551da177e4SLinus Torvalds 35677bf4400SJeff Dike return start_uml(); 3571da177e4SLinus Torvalds } 3581da177e4SLinus Torvalds 3595b4236e1SMasami Hiramatsu int __init __weak read_initrd(void) 3605b4236e1SMasami Hiramatsu { 3615b4236e1SMasami Hiramatsu return 0; 3625b4236e1SMasami Hiramatsu } 3635b4236e1SMasami Hiramatsu 3641da177e4SLinus Torvalds void __init setup_arch(char **cmdline_p) 3651da177e4SLinus Torvalds { 366b6323697SRichard Weinberger stack_protections((unsigned long) &init_thread_info); 367b6323697SRichard Weinberger setup_physmem(uml_physmem, uml_reserved, physmem_size, highmem); 368b6323697SRichard Weinberger mem_total_pages(physmem_size, iomem_size, highmem); 3695b4236e1SMasami Hiramatsu read_initrd(); 370b6323697SRichard Weinberger 3711da177e4SLinus Torvalds paging_init(); 37219bf7e7aSAlon Bar-Lev strlcpy(boot_command_line, command_line, COMMAND_LINE_SIZE); 3731da177e4SLinus Torvalds *cmdline_p = command_line; 374b4ffb6adSJeff Dike setup_hostinfo(host_info, sizeof host_info); 3751da177e4SLinus Torvalds } 3761da177e4SLinus Torvalds 3771da177e4SLinus Torvalds void __init check_bugs(void) 3781da177e4SLinus Torvalds { 3791da177e4SLinus Torvalds arch_check_bugs(); 3808e367065SJeff Dike os_check_bugs(); 3811da177e4SLinus Torvalds } 3821da177e4SLinus Torvalds 3839a0b5817SGerd Hoffmann void apply_alternatives(struct alt_instr *start, struct alt_instr *end) 3849a0b5817SGerd Hoffmann { 3859a0b5817SGerd Hoffmann } 38688fc078aSPeter Zijlstra 38788fc078aSPeter Zijlstra void *text_poke(void *addr, const void *opcode, size_t len) 38888fc078aSPeter Zijlstra { 38988fc078aSPeter Zijlstra /* 39088fc078aSPeter Zijlstra * In UML, the only reference to this function is in 39188fc078aSPeter Zijlstra * apply_relocate_add(), which shouldn't ever actually call this 39288fc078aSPeter Zijlstra * because UML doesn't have live patching. 39388fc078aSPeter Zijlstra */ 39488fc078aSPeter Zijlstra WARN_ON(1); 39588fc078aSPeter Zijlstra 39688fc078aSPeter Zijlstra return memcpy(addr, opcode, len); 39788fc078aSPeter Zijlstra } 39888fc078aSPeter Zijlstra 39988fc078aSPeter Zijlstra void text_poke_sync(void) 40088fc078aSPeter Zijlstra { 40188fc078aSPeter Zijlstra } 40292dcd3d3SJohannes Berg 40392dcd3d3SJohannes Berg void uml_pm_wake(void) 40492dcd3d3SJohannes Berg { 40592dcd3d3SJohannes Berg pm_system_wakeup(); 40692dcd3d3SJohannes Berg } 40792dcd3d3SJohannes Berg 4081fb1abc8SJohannes Berg #ifdef CONFIG_PM_SLEEP 409a374b7cbSJohannes Berg static int um_suspend_valid(suspend_state_t state) 410a374b7cbSJohannes Berg { 411a374b7cbSJohannes Berg return state == PM_SUSPEND_MEM; 412a374b7cbSJohannes Berg } 413a374b7cbSJohannes Berg 414a374b7cbSJohannes Berg static int um_suspend_prepare(void) 415a374b7cbSJohannes Berg { 416a374b7cbSJohannes Berg um_irqs_suspend(); 417a374b7cbSJohannes Berg return 0; 418a374b7cbSJohannes Berg } 419a374b7cbSJohannes Berg 420a374b7cbSJohannes Berg static int um_suspend_enter(suspend_state_t state) 421a374b7cbSJohannes Berg { 422a374b7cbSJohannes Berg if (WARN_ON(state != PM_SUSPEND_MEM)) 423a374b7cbSJohannes Berg return -EINVAL; 424a374b7cbSJohannes Berg 425a374b7cbSJohannes Berg /* 426a374b7cbSJohannes Berg * This is identical to the idle sleep, but we've just 427a374b7cbSJohannes Berg * (during suspend) turned off all interrupt sources 428a374b7cbSJohannes Berg * except for the ones we want, so now we can only wake 429a374b7cbSJohannes Berg * up on something we actually want to wake up on. All 430a374b7cbSJohannes Berg * timing has also been suspended. 431a374b7cbSJohannes Berg */ 432a374b7cbSJohannes Berg um_idle_sleep(); 433a374b7cbSJohannes Berg return 0; 434a374b7cbSJohannes Berg } 435a374b7cbSJohannes Berg 436a374b7cbSJohannes Berg static void um_suspend_finish(void) 437a374b7cbSJohannes Berg { 438a374b7cbSJohannes Berg um_irqs_resume(); 439a374b7cbSJohannes Berg } 440a374b7cbSJohannes Berg 441a374b7cbSJohannes Berg const struct platform_suspend_ops um_suspend_ops = { 442a374b7cbSJohannes Berg .valid = um_suspend_valid, 443a374b7cbSJohannes Berg .prepare = um_suspend_prepare, 444a374b7cbSJohannes Berg .enter = um_suspend_enter, 445a374b7cbSJohannes Berg .finish = um_suspend_finish, 446a374b7cbSJohannes Berg }; 447a374b7cbSJohannes Berg 44892dcd3d3SJohannes Berg static int init_pm_wake_signal(void) 44992dcd3d3SJohannes Berg { 45092dcd3d3SJohannes Berg /* 45192dcd3d3SJohannes Berg * In external time-travel mode we can't use signals to wake up 45292dcd3d3SJohannes Berg * since that would mess with the scheduling. We'll have to do 45392dcd3d3SJohannes Berg * some additional work to support wakeup on virtio devices or 45492dcd3d3SJohannes Berg * similar, perhaps implementing a fake RTC controller that can 45592dcd3d3SJohannes Berg * trigger wakeup (and request the appropriate scheduling from 45692dcd3d3SJohannes Berg * the external scheduler when going to suspend.) 45792dcd3d3SJohannes Berg */ 45892dcd3d3SJohannes Berg if (time_travel_mode != TT_MODE_EXTERNAL) 45992dcd3d3SJohannes Berg register_pm_wake_signal(); 460a374b7cbSJohannes Berg 461a374b7cbSJohannes Berg suspend_set_ops(&um_suspend_ops); 462a374b7cbSJohannes Berg 46392dcd3d3SJohannes Berg return 0; 46492dcd3d3SJohannes Berg } 46592dcd3d3SJohannes Berg 46692dcd3d3SJohannes Berg late_initcall(init_pm_wake_signal); 46792dcd3d3SJohannes Berg #endif 468