linux/arch/um/kernel/um_arch.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
   4 */
   5
   6#include <linux/delay.h>
   7#include <linux/init.h>
   8#include <linux/mm.h>
   9#include <linux/module.h>
  10#include <linux/seq_file.h>
  11#include <linux/string.h>
  12#include <linux/utsname.h>
  13#include <linux/sched.h>
  14#include <linux/sched/task.h>
  15#include <linux/kmsg_dump.h>
  16#include <linux/suspend.h>
  17
  18#include <asm/processor.h>
  19#include <asm/sections.h>
  20#include <asm/setup.h>
  21#include <as-layout.h>
  22#include <arch.h>
  23#include <init.h>
  24#include <kern.h>
  25#include <kern_util.h>
  26#include <mem_user.h>
  27#include <os.h>
  28
  29#define DEFAULT_COMMAND_LINE_ROOT "root=98:0"
  30#define DEFAULT_COMMAND_LINE_CONSOLE "console=tty"
  31
  32/* Changed in add_arg and setup_arch, which run before SMP is started */
  33static char __initdata command_line[COMMAND_LINE_SIZE] = { 0 };
  34
  35static void __init add_arg(char *arg)
  36{
  37        if (strlen(command_line) + strlen(arg) + 1 > COMMAND_LINE_SIZE) {
  38                os_warn("add_arg: Too many command line arguments!\n");
  39                exit(1);
  40        }
  41        if (strlen(command_line) > 0)
  42                strcat(command_line, " ");
  43        strcat(command_line, arg);
  44}
  45
  46/*
  47 * These fields are initialized at boot time and not changed.
  48 * XXX This structure is used only in the non-SMP case.  Maybe this
  49 * should be moved to smp.c.
  50 */
  51struct cpuinfo_um boot_cpu_data = {
  52        .loops_per_jiffy        = 0,
  53        .ipi_pipe               = { -1, -1 }
  54};
  55
  56union thread_union cpu0_irqstack
  57        __section(".data..init_irqstack") =
  58                { .thread_info = INIT_THREAD_INFO(init_task) };
  59
  60/* Changed in setup_arch, which is called in early boot */
  61static char host_info[(__NEW_UTS_LEN + 1) * 5];
  62
  63static int show_cpuinfo(struct seq_file *m, void *v)
  64{
  65        int index = 0;
  66
  67        seq_printf(m, "processor\t: %d\n", index);
  68        seq_printf(m, "vendor_id\t: User Mode Linux\n");
  69        seq_printf(m, "model name\t: UML\n");
  70        seq_printf(m, "mode\t\t: skas\n");
  71        seq_printf(m, "host\t\t: %s\n", host_info);
  72        seq_printf(m, "bogomips\t: %lu.%02lu\n\n",
  73                   loops_per_jiffy/(500000/HZ),
  74                   (loops_per_jiffy/(5000/HZ)) % 100);
  75
  76        return 0;
  77}
  78
  79static void *c_start(struct seq_file *m, loff_t *pos)
  80{
  81        return *pos < NR_CPUS ? cpu_data + *pos : NULL;
  82}
  83
  84static void *c_next(struct seq_file *m, void *v, loff_t *pos)
  85{
  86        ++*pos;
  87        return c_start(m, pos);
  88}
  89
  90static void c_stop(struct seq_file *m, void *v)
  91{
  92}
  93
  94const struct seq_operations cpuinfo_op = {
  95        .start  = c_start,
  96        .next   = c_next,
  97        .stop   = c_stop,
  98        .show   = show_cpuinfo,
  99};
 100
 101/* Set in linux_main */
 102unsigned long uml_physmem;
 103EXPORT_SYMBOL(uml_physmem);
 104
 105unsigned long uml_reserved; /* Also modified in mem_init */
 106unsigned long start_vm;
 107unsigned long end_vm;
 108
 109/* Set in uml_ncpus_setup */
 110int ncpus = 1;
 111
 112/* Set in early boot */
 113static int have_root __initdata;
 114static int have_console __initdata;
 115
 116/* Set in uml_mem_setup and modified in linux_main */
 117long long physmem_size = 32 * 1024 * 1024;
 118EXPORT_SYMBOL(physmem_size);
 119
 120static const char *usage_string =
 121"User Mode Linux v%s\n"
 122"       available at http://user-mode-linux.sourceforge.net/\n\n";
 123
 124static int __init uml_version_setup(char *line, int *add)
 125{
 126        /* Explicitly use printf() to show version in stdout */
 127        printf("%s\n", init_utsname()->release);
 128        exit(0);
 129
 130        return 0;
 131}
 132
 133__uml_setup("--version", uml_version_setup,
 134"--version\n"
 135"    Prints the version number of the kernel.\n\n"
 136);
 137
 138static int __init uml_root_setup(char *line, int *add)
 139{
 140        have_root = 1;
 141        return 0;
 142}
 143
 144__uml_setup("root=", uml_root_setup,
 145"root=<file containing the root fs>\n"
 146"    This is actually used by the generic kernel in exactly the same\n"
 147"    way as in any other kernel. If you configure a number of block\n"
 148"    devices and want to boot off something other than ubd0, you \n"
 149"    would use something like:\n"
 150"        root=/dev/ubd5\n\n"
 151);
 152
 153static int __init no_skas_debug_setup(char *line, int *add)
 154{
 155        os_warn("'debug' is not necessary to gdb UML in skas mode - run\n");
 156        os_warn("'gdb linux'\n");
 157
 158        return 0;
 159}
 160
 161__uml_setup("debug", no_skas_debug_setup,
 162"debug\n"
 163"    this flag is not needed to run gdb on UML in skas mode\n\n"
 164);
 165
 166static int __init uml_console_setup(char *line, int *add)
 167{
 168        have_console = 1;
 169        return 0;
 170}
 171
 172__uml_setup("console=", uml_console_setup,
 173"console=<preferred console>\n"
 174"    Specify the preferred console output driver\n\n"
 175);
 176
 177static int __init Usage(char *line, int *add)
 178{
 179        const char **p;
 180
 181        printf(usage_string, init_utsname()->release);
 182        p = &__uml_help_start;
 183        /* Explicitly use printf() to show help in stdout */
 184        while (p < &__uml_help_end) {
 185                printf("%s", *p);
 186                p++;
 187        }
 188        exit(0);
 189        return 0;
 190}
 191
 192__uml_setup("--help", Usage,
 193"--help\n"
 194"    Prints this message.\n\n"
 195);
 196
 197static void __init uml_checksetup(char *line, int *add)
 198{
 199        struct uml_param *p;
 200
 201        p = &__uml_setup_start;
 202        while (p < &__uml_setup_end) {
 203                size_t n;
 204
 205                n = strlen(p->str);
 206                if (!strncmp(line, p->str, n) && p->setup_func(line + n, add))
 207                        return;
 208                p++;
 209        }
 210}
 211
 212static void __init uml_postsetup(void)
 213{
 214        initcall_t *p;
 215
 216        p = &__uml_postsetup_start;
 217        while (p < &__uml_postsetup_end) {
 218                (*p)();
 219                p++;
 220        }
 221        return;
 222}
 223
 224static int panic_exit(struct notifier_block *self, unsigned long unused1,
 225                      void *unused2)
 226{
 227        kmsg_dump(KMSG_DUMP_PANIC);
 228        bust_spinlocks(1);
 229        bust_spinlocks(0);
 230        uml_exitcode = 1;
 231        os_dump_core();
 232        return 0;
 233}
 234
 235static struct notifier_block panic_exit_notifier = {
 236        .notifier_call          = panic_exit,
 237        .next                   = NULL,
 238        .priority               = 0
 239};
 240
 241void uml_finishsetup(void)
 242{
 243        atomic_notifier_chain_register(&panic_notifier_list,
 244                                       &panic_exit_notifier);
 245
 246        uml_postsetup();
 247
 248        new_thread_handler();
 249}
 250
 251/* Set during early boot */
 252unsigned long task_size;
 253EXPORT_SYMBOL(task_size);
 254
 255unsigned long host_task_size;
 256
 257unsigned long brk_start;
 258unsigned long end_iomem;
 259EXPORT_SYMBOL(end_iomem);
 260
 261#define MIN_VMALLOC (32 * 1024 * 1024)
 262
 263int __init linux_main(int argc, char **argv)
 264{
 265        unsigned long avail, diff;
 266        unsigned long virtmem_size, max_physmem;
 267        unsigned long stack;
 268        unsigned int i;
 269        int add;
 270
 271        for (i = 1; i < argc; i++) {
 272                if ((i == 1) && (argv[i][0] == ' '))
 273                        continue;
 274                add = 1;
 275                uml_checksetup(argv[i], &add);
 276                if (add)
 277                        add_arg(argv[i]);
 278        }
 279        if (have_root == 0)
 280                add_arg(DEFAULT_COMMAND_LINE_ROOT);
 281
 282        if (have_console == 0)
 283                add_arg(DEFAULT_COMMAND_LINE_CONSOLE);
 284
 285        host_task_size = os_get_top_address();
 286        /*
 287         * TASK_SIZE needs to be PGDIR_SIZE aligned or else exit_mmap craps
 288         * out
 289         */
 290        task_size = host_task_size & PGDIR_MASK;
 291
 292        /* OS sanity checks that need to happen before the kernel runs */
 293        os_early_checks();
 294
 295        brk_start = (unsigned long) sbrk(0);
 296
 297        /*
 298         * Increase physical memory size for exec-shield users
 299         * so they actually get what they asked for. This should
 300         * add zero for non-exec shield users
 301         */
 302
 303        diff = UML_ROUND_UP(brk_start) - UML_ROUND_UP(&_end);
 304        if (diff > 1024 * 1024) {
 305                os_info("Adding %ld bytes to physical memory to account for "
 306                        "exec-shield gap\n", diff);
 307                physmem_size += UML_ROUND_UP(brk_start) - UML_ROUND_UP(&_end);
 308        }
 309
 310        uml_physmem = (unsigned long) __binary_start & PAGE_MASK;
 311
 312        /* Reserve up to 4M after the current brk */
 313        uml_reserved = ROUND_4M(brk_start) + (1 << 22);
 314
 315        setup_machinename(init_utsname()->machine);
 316
 317        highmem = 0;
 318        iomem_size = (iomem_size + PAGE_SIZE - 1) & PAGE_MASK;
 319        max_physmem = TASK_SIZE - uml_physmem - iomem_size - MIN_VMALLOC;
 320
 321        /*
 322         * Zones have to begin on a 1 << MAX_ORDER page boundary,
 323         * so this makes sure that's true for highmem
 324         */
 325        max_physmem &= ~((1 << (PAGE_SHIFT + MAX_ORDER)) - 1);
 326        if (physmem_size + iomem_size > max_physmem) {
 327                highmem = physmem_size + iomem_size - max_physmem;
 328                physmem_size -= highmem;
 329        }
 330
 331        high_physmem = uml_physmem + physmem_size;
 332        end_iomem = high_physmem + iomem_size;
 333        high_memory = (void *) end_iomem;
 334
 335        start_vm = VMALLOC_START;
 336
 337        virtmem_size = physmem_size;
 338        stack = (unsigned long) argv;
 339        stack &= ~(1024 * 1024 - 1);
 340        avail = stack - start_vm;
 341        if (physmem_size > avail)
 342                virtmem_size = avail;
 343        end_vm = start_vm + virtmem_size;
 344
 345        if (virtmem_size < physmem_size)
 346                os_info("Kernel virtual memory size shrunk to %lu bytes\n",
 347                        virtmem_size);
 348
 349        os_flush_stdout();
 350
 351        return start_uml();
 352}
 353
 354int __init __weak read_initrd(void)
 355{
 356        return 0;
 357}
 358
 359void __init setup_arch(char **cmdline_p)
 360{
 361        stack_protections((unsigned long) &init_thread_info);
 362        setup_physmem(uml_physmem, uml_reserved, physmem_size, highmem);
 363        mem_total_pages(physmem_size, iomem_size, highmem);
 364        read_initrd();
 365
 366        paging_init();
 367        strlcpy(boot_command_line, command_line, COMMAND_LINE_SIZE);
 368        *cmdline_p = command_line;
 369        setup_hostinfo(host_info, sizeof host_info);
 370}
 371
 372void __init check_bugs(void)
 373{
 374        arch_check_bugs();
 375        os_check_bugs();
 376}
 377
 378void apply_alternatives(struct alt_instr *start, struct alt_instr *end)
 379{
 380}
 381
 382void *text_poke(void *addr, const void *opcode, size_t len)
 383{
 384        /*
 385         * In UML, the only reference to this function is in
 386         * apply_relocate_add(), which shouldn't ever actually call this
 387         * because UML doesn't have live patching.
 388         */
 389        WARN_ON(1);
 390
 391        return memcpy(addr, opcode, len);
 392}
 393
 394void text_poke_sync(void)
 395{
 396}
 397
 398void uml_pm_wake(void)
 399{
 400        pm_system_wakeup();
 401}
 402
 403#ifdef CONFIG_PM_SLEEP
 404static int um_suspend_valid(suspend_state_t state)
 405{
 406        return state == PM_SUSPEND_MEM;
 407}
 408
 409static int um_suspend_prepare(void)
 410{
 411        um_irqs_suspend();
 412        return 0;
 413}
 414
 415static int um_suspend_enter(suspend_state_t state)
 416{
 417        if (WARN_ON(state != PM_SUSPEND_MEM))
 418                return -EINVAL;
 419
 420        /*
 421         * This is identical to the idle sleep, but we've just
 422         * (during suspend) turned off all interrupt sources
 423         * except for the ones we want, so now we can only wake
 424         * up on something we actually want to wake up on. All
 425         * timing has also been suspended.
 426         */
 427        um_idle_sleep();
 428        return 0;
 429}
 430
 431static void um_suspend_finish(void)
 432{
 433        um_irqs_resume();
 434}
 435
 436const struct platform_suspend_ops um_suspend_ops = {
 437        .valid = um_suspend_valid,
 438        .prepare = um_suspend_prepare,
 439        .enter = um_suspend_enter,
 440        .finish = um_suspend_finish,
 441};
 442
 443static int init_pm_wake_signal(void)
 444{
 445        /*
 446         * In external time-travel mode we can't use signals to wake up
 447         * since that would mess with the scheduling. We'll have to do
 448         * some additional work to support wakeup on virtio devices or
 449         * similar, perhaps implementing a fake RTC controller that can
 450         * trigger wakeup (and request the appropriate scheduling from
 451         * the external scheduler when going to suspend.)
 452         */
 453        if (time_travel_mode != TT_MODE_EXTERNAL)
 454                register_pm_wake_signal();
 455
 456        suspend_set_ops(&um_suspend_ops);
 457
 458        return 0;
 459}
 460
 461late_initcall(init_pm_wake_signal);
 462#endif
 463