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