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