um_arch.c 10.3 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
2
/*
Jeff Dike's avatar
Jeff Dike committed
3
 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
Linus Torvalds's avatar
Linus Torvalds committed
4 5
 */

6 7 8 9 10 11 12
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/string.h>
#include <linux/utsname.h>
Thomas Gleixner's avatar
Thomas Gleixner committed
13
#include <linux/sched.h>
14
#include <linux/sched/task.h>
Thomas Meyer's avatar
Thomas Meyer committed
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 22 23 24 25 26 27
#include <as-layout.h>
#include <arch.h>
#include <init.h>
#include <kern.h>
#include <kern_util.h>
#include <mem_user.h>
#include <os.h>
Linus Torvalds's avatar
Linus Torvalds committed
28

29 30
#define DEFAULT_COMMAND_LINE_ROOT "root=98:0"
#define DEFAULT_COMMAND_LINE_CONSOLE "console=tty"
Linus Torvalds's avatar
Linus Torvalds committed
31

Jeff Dike's avatar
Jeff Dike committed
32
/* Changed in add_arg and setup_arch, which run before SMP is started */
33
static char __initdata command_line[COMMAND_LINE_SIZE] = { 0 };
Linus Torvalds's avatar
Linus Torvalds committed
34

35
static void __init add_arg(char *arg)
Linus Torvalds's avatar
Linus Torvalds committed
36 37
{
	if (strlen(command_line) + strlen(arg) + 1 > COMMAND_LINE_SIZE) {
38
		os_warn("add_arg: Too many command line arguments!\n");
Linus Torvalds's avatar
Linus Torvalds committed
39 40
		exit(1);
	}
Jeff Dike's avatar
Jeff Dike committed
41
	if (strlen(command_line) > 0)
Linus Torvalds's avatar
Linus Torvalds committed
42 43 44 45
		strcat(command_line, " ");
	strcat(command_line, arg);
}

Jeff Dike's avatar
Jeff Dike committed
46 47 48 49 50 51
/*
 * These fields are initialized at boot time and not changed.
 * XXX This structure is used only in the non-SMP case.  Maybe this
 * should be moved to smp.c.
 */
struct cpuinfo_um boot_cpu_data = {
Linus Torvalds's avatar
Linus Torvalds committed
52 53 54 55
	.loops_per_jiffy	= 0,
	.ipi_pipe		= { -1, -1 }
};

Thomas Gleixner's avatar
Thomas Gleixner committed
56
union thread_union cpu0_irqstack
57
	__section(".data..init_irqstack") =
58
		{ .thread_info = INIT_THREAD_INFO(init_task) };
Thomas Gleixner's avatar
Thomas Gleixner committed
59

Jeff Dike's avatar
Jeff Dike committed
60 61 62
/* Changed in setup_arch, which is called in early boot */
static char host_info[(__NEW_UTS_LEN + 1) * 5];

Linus Torvalds's avatar
Linus Torvalds committed
63 64 65 66 67 68 69
static int show_cpuinfo(struct seq_file *m, void *v)
{
	int index = 0;

	seq_printf(m, "processor\t: %d\n", index);
	seq_printf(m, "vendor_id\t: User Mode Linux\n");
	seq_printf(m, "model name\t: UML\n");
Jeff Dike's avatar
Jeff Dike committed
70
	seq_printf(m, "mode\t\t: skas\n");
Linus Torvalds's avatar
Linus Torvalds committed
71 72 73 74 75
	seq_printf(m, "host\t\t: %s\n", host_info);
	seq_printf(m, "bogomips\t: %lu.%02lu\n\n",
		   loops_per_jiffy/(500000/HZ),
		   (loops_per_jiffy/(5000/HZ)) % 100);

Jeff Dike's avatar
Jeff Dike committed
76
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
}

static void *c_start(struct seq_file *m, loff_t *pos)
{
	return *pos < NR_CPUS ? cpu_data + *pos : NULL;
}

static void *c_next(struct seq_file *m, void *v, loff_t *pos)
{
	++*pos;
	return c_start(m, pos);
}

static void c_stop(struct seq_file *m, void *v)
{
}

Jeff Dike's avatar
Jeff Dike committed
94
const struct seq_operations cpuinfo_op = {
Linus Torvalds's avatar
Linus Torvalds committed
95 96 97 98 99 100 101 102
	.start	= c_start,
	.next	= c_next,
	.stop	= c_stop,
	.show	= show_cpuinfo,
};

/* Set in linux_main */
unsigned long uml_physmem;
103 104
EXPORT_SYMBOL(uml_physmem);

Jeff Dike's avatar
Jeff Dike committed
105
unsigned long uml_reserved; /* Also modified in mem_init */
Linus Torvalds's avatar
Linus Torvalds committed
106 107
unsigned long start_vm;
unsigned long end_vm;
Jeff Dike's avatar
Jeff Dike committed
108 109

/* Set in uml_ncpus_setup */
Linus Torvalds's avatar
Linus Torvalds committed
110 111 112
int ncpus = 1;

/* Set in early boot */
113 114
static int have_root __initdata;
static int have_console __initdata;
Jeff Dike's avatar
Jeff Dike committed
115 116

/* Set in uml_mem_setup and modified in linux_main */
Jeff Dike's avatar
Jeff Dike committed
117
long long physmem_size = 32 * 1024 * 1024;
118
EXPORT_SYMBOL(physmem_size);
Linus Torvalds's avatar
Linus Torvalds committed
119

120
static const char *usage_string =
Linus Torvalds's avatar
Linus Torvalds committed
121 122 123 124 125
"User Mode Linux v%s\n"
"	available at http://user-mode-linux.sourceforge.net/\n\n";

static int __init uml_version_setup(char *line, int *add)
{
126
	/* Explicitly use printf() to show version in stdout */
127
	printf("%s\n", init_utsname()->release);
Linus Torvalds's avatar
Linus Torvalds committed
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
	exit(0);

	return 0;
}

__uml_setup("--version", uml_version_setup,
"--version\n"
"    Prints the version number of the kernel.\n\n"
);

static int __init uml_root_setup(char *line, int *add)
{
	have_root = 1;
	return 0;
}

__uml_setup("root=", uml_root_setup,
"root=<file containing the root fs>\n"
"    This is actually used by the generic kernel in exactly the same\n"
"    way as in any other kernel. If you configure a number of block\n"
"    devices and want to boot off something other than ubd0, you \n"
"    would use something like:\n"
"        root=/dev/ubd5\n\n"
);

153 154
static int __init no_skas_debug_setup(char *line, int *add)
{
155 156
	os_warn("'debug' is not necessary to gdb UML in skas mode - run\n");
	os_warn("'gdb linux'\n");
157 158 159 160 161 162 163 164 165

	return 0;
}

__uml_setup("debug", no_skas_debug_setup,
"debug\n"
"    this flag is not needed to run gdb on UML in skas mode\n\n"
);

166 167 168 169 170 171 172 173 174 175 176
static int __init uml_console_setup(char *line, int *add)
{
	have_console = 1;
	return 0;
}

__uml_setup("console=", uml_console_setup,
"console=<preferred console>\n"
"    Specify the preferred console output driver\n\n"
);

Linus Torvalds's avatar
Linus Torvalds committed
177 178
static int __init Usage(char *line, int *add)
{
Jeff Dike's avatar
Jeff Dike committed
179
	const char **p;
Linus Torvalds's avatar
Linus Torvalds committed
180

181
	printf(usage_string, init_utsname()->release);
Jeff Dike's avatar
Jeff Dike committed
182
	p = &__uml_help_start;
183
	/* Explicitly use printf() to show help in stdout */
Jeff Dike's avatar
Jeff Dike committed
184 185 186 187
	while (p < &__uml_help_end) {
		printf("%s", *p);
		p++;
	}
Linus Torvalds's avatar
Linus Torvalds committed
188 189 190 191 192 193 194 195 196
	exit(0);
	return 0;
}

__uml_setup("--help", Usage,
"--help\n"
"    Prints this message.\n\n"
);

197
static void __init uml_checksetup(char *line, int *add)
Linus Torvalds's avatar
Linus Torvalds committed
198 199 200 201
{
	struct uml_param *p;

	p = &__uml_setup_start;
202
	while (p < &__uml_setup_end) {
203
		size_t n;
Linus Torvalds's avatar
Linus Torvalds committed
204 205

		n = strlen(p->str);
Jeff Dike's avatar
Jeff Dike committed
206
		if (!strncmp(line, p->str, n) && p->setup_func(line + n, add))
207
			return;
Linus Torvalds's avatar
Linus Torvalds committed
208 209 210 211 212 213 214 215 216
		p++;
	}
}

static void __init uml_postsetup(void)
{
	initcall_t *p;

	p = &__uml_postsetup_start;
217
	while (p < &__uml_postsetup_end) {
Linus Torvalds's avatar
Linus Torvalds committed
218 219 220 221 222 223
		(*p)();
		p++;
	}
	return;
}

224 225 226
static int panic_exit(struct notifier_block *self, unsigned long unused1,
		      void *unused2)
{
Thomas Meyer's avatar
Thomas Meyer committed
227
	kmsg_dump(KMSG_DUMP_PANIC);
228 229 230 231 232 233 234 235 236 237 238 239 240
	bust_spinlocks(1);
	bust_spinlocks(0);
	uml_exitcode = 1;
	os_dump_core();
	return 0;
}

static struct notifier_block panic_exit_notifier = {
	.notifier_call 		= panic_exit,
	.next 			= NULL,
	.priority 		= 0
};

241 242 243 244 245 246 247 248 249 250
void uml_finishsetup(void)
{
	atomic_notifier_chain_register(&panic_notifier_list,
				       &panic_exit_notifier);

	uml_postsetup();

	new_thread_handler();
}

Linus Torvalds's avatar
Linus Torvalds committed
251
/* Set during early boot */
252
unsigned long stub_start;
253 254 255 256 257
unsigned long task_size;
EXPORT_SYMBOL(task_size);

unsigned long host_task_size;

Linus Torvalds's avatar
Linus Torvalds committed
258 259 260 261 262 263
unsigned long brk_start;
unsigned long end_iomem;
EXPORT_SYMBOL(end_iomem);

#define MIN_VMALLOC (32 * 1024 * 1024)

264
int __init linux_main(int argc, char **argv)
Linus Torvalds's avatar
Linus Torvalds committed
265 266 267
{
	unsigned long avail, diff;
	unsigned long virtmem_size, max_physmem;
268
	unsigned long stack;
269 270
	unsigned int i;
	int add;
Linus Torvalds's avatar
Linus Torvalds committed
271

Jeff Dike's avatar
Jeff Dike committed
272 273 274
	for (i = 1; i < argc; i++) {
		if ((i == 1) && (argv[i][0] == ' '))
			continue;
Linus Torvalds's avatar
Linus Torvalds committed
275 276 277 278 279
		add = 1;
		uml_checksetup(argv[i], &add);
		if (add)
			add_arg(argv[i]);
	}
Jeff Dike's avatar
Jeff Dike committed
280
	if (have_root == 0)
281 282 283 284
		add_arg(DEFAULT_COMMAND_LINE_ROOT);

	if (have_console == 0)
		add_arg(DEFAULT_COMMAND_LINE_CONSOLE);
Linus Torvalds's avatar
Linus Torvalds committed
285

286
	host_task_size = os_get_top_address();
287 288 289 290
	/* reserve two pages for the stubs */
	host_task_size -= 2 * PAGE_SIZE;
	stub_start = host_task_size;

291 292 293 294 295 296
	/*
	 * TASK_SIZE needs to be PGDIR_SIZE aligned or else exit_mmap craps
	 * out
	 */
	task_size = host_task_size & PGDIR_MASK;

Jeff Dike's avatar
Jeff Dike committed
297
	/* OS sanity checks that need to happen before the kernel runs */
298
	os_early_checks();
299

Linus Torvalds's avatar
Linus Torvalds committed
300
	brk_start = (unsigned long) sbrk(0);
301

Jeff Dike's avatar
Jeff Dike committed
302 303 304 305 306
	/*
	 * Increase physical memory size for exec-shield users
	 * so they actually get what they asked for. This should
	 * add zero for non-exec shield users
	 */
Linus Torvalds's avatar
Linus Torvalds committed
307 308

	diff = UML_ROUND_UP(brk_start) - UML_ROUND_UP(&_end);
Jeff Dike's avatar
Jeff Dike committed
309
	if (diff > 1024 * 1024) {
310 311
		os_info("Adding %ld bytes to physical memory to account for "
			"exec-shield gap\n", diff);
Linus Torvalds's avatar
Linus Torvalds committed
312 313 314
		physmem_size += UML_ROUND_UP(brk_start) - UML_ROUND_UP(&_end);
	}

315
	uml_physmem = (unsigned long) __binary_start & PAGE_MASK;
Linus Torvalds's avatar
Linus Torvalds committed
316 317 318 319

	/* Reserve up to 4M after the current brk */
	uml_reserved = ROUND_4M(brk_start) + (1 << 22);

320
	setup_machinename(init_utsname()->machine);
Linus Torvalds's avatar
Linus Torvalds committed
321 322 323

	highmem = 0;
	iomem_size = (iomem_size + PAGE_SIZE - 1) & PAGE_MASK;
324
	max_physmem = TASK_SIZE - uml_physmem - iomem_size - MIN_VMALLOC;
Linus Torvalds's avatar
Linus Torvalds committed
325

Jeff Dike's avatar
Jeff Dike committed
326 327
	/*
	 * Zones have to begin on a 1 << MAX_ORDER page boundary,
Linus Torvalds's avatar
Linus Torvalds committed
328 329 330
	 * so this makes sure that's true for highmem
	 */
	max_physmem &= ~((1 << (PAGE_SHIFT + MAX_ORDER)) - 1);
Jeff Dike's avatar
Jeff Dike committed
331
	if (physmem_size + iomem_size > max_physmem) {
Linus Torvalds's avatar
Linus Torvalds committed
332 333 334 335 336 337 338 339 340 341 342
		highmem = physmem_size + iomem_size - max_physmem;
		physmem_size -= highmem;
	}

	high_physmem = uml_physmem + physmem_size;
	end_iomem = high_physmem + iomem_size;
	high_memory = (void *) end_iomem;

	start_vm = VMALLOC_START;

	virtmem_size = physmem_size;
343 344 345
	stack = (unsigned long) argv;
	stack &= ~(1024 * 1024 - 1);
	avail = stack - start_vm;
Jeff Dike's avatar
Jeff Dike committed
346 347
	if (physmem_size > avail)
		virtmem_size = avail;
Linus Torvalds's avatar
Linus Torvalds committed
348 349
	end_vm = start_vm + virtmem_size;

Jeff Dike's avatar
Jeff Dike committed
350
	if (virtmem_size < physmem_size)
351 352
		os_info("Kernel virtual memory size shrunk to %lu bytes\n",
			virtmem_size);
Linus Torvalds's avatar
Linus Torvalds committed
353 354 355

	os_flush_stdout();

356
	return start_uml();
Linus Torvalds's avatar
Linus Torvalds committed
357 358
}

359 360 361 362 363
int __init __weak read_initrd(void)
{
	return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
364 365
void __init setup_arch(char **cmdline_p)
{
366 367 368
	stack_protections((unsigned long) &init_thread_info);
	setup_physmem(uml_physmem, uml_reserved, physmem_size, highmem);
	mem_total_pages(physmem_size, iomem_size, highmem);
369
	read_initrd();
370

Linus Torvalds's avatar
Linus Torvalds committed
371
	paging_init();
372
	strlcpy(boot_command_line, command_line, COMMAND_LINE_SIZE);
Jeff Dike's avatar
Jeff Dike committed
373
	*cmdline_p = command_line;
Jeff Dike's avatar
Jeff Dike committed
374
	setup_hostinfo(host_info, sizeof host_info);
Linus Torvalds's avatar
Linus Torvalds committed
375 376 377 378 379
}

void __init check_bugs(void)
{
	arch_check_bugs();
Jeff Dike's avatar
Jeff Dike committed
380
	os_check_bugs();
Linus Torvalds's avatar
Linus Torvalds committed
381 382
}

383 384 385
void apply_alternatives(struct alt_instr *start, struct alt_instr *end)
{
}
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401

void *text_poke(void *addr, const void *opcode, size_t len)
{
	/*
	 * In UML, the only reference to this function is in
	 * apply_relocate_add(), which shouldn't ever actually call this
	 * because UML doesn't have live patching.
	 */
	WARN_ON(1);

	return memcpy(addr, opcode, len);
}

void text_poke_sync(void)
{
}
402 403 404 405 406 407

void uml_pm_wake(void)
{
	pm_system_wakeup();
}

408
#ifdef CONFIG_PM_SLEEP
Johannes Berg's avatar
Johannes Berg committed
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
static int um_suspend_valid(suspend_state_t state)
{
	return state == PM_SUSPEND_MEM;
}

static int um_suspend_prepare(void)
{
	um_irqs_suspend();
	return 0;
}

static int um_suspend_enter(suspend_state_t state)
{
	if (WARN_ON(state != PM_SUSPEND_MEM))
		return -EINVAL;

	/*
	 * This is identical to the idle sleep, but we've just
	 * (during suspend) turned off all interrupt sources
	 * except for the ones we want, so now we can only wake
	 * up on something we actually want to wake up on. All
	 * timing has also been suspended.
	 */
	um_idle_sleep();
	return 0;
}

static void um_suspend_finish(void)
{
	um_irqs_resume();
}

const struct platform_suspend_ops um_suspend_ops = {
	.valid = um_suspend_valid,
	.prepare = um_suspend_prepare,
	.enter = um_suspend_enter,
	.finish = um_suspend_finish,
};

448 449 450 451 452 453 454 455 456 457 458 459
static int init_pm_wake_signal(void)
{
	/*
	 * In external time-travel mode we can't use signals to wake up
	 * since that would mess with the scheduling. We'll have to do
	 * some additional work to support wakeup on virtio devices or
	 * similar, perhaps implementing a fake RTC controller that can
	 * trigger wakeup (and request the appropriate scheduling from
	 * the external scheduler when going to suspend.)
	 */
	if (time_travel_mode != TT_MODE_EXTERNAL)
		register_pm_wake_signal();
Johannes Berg's avatar
Johannes Berg committed
460 461 462

	suspend_set_ops(&um_suspend_ops);

463 464 465 466 467
	return 0;
}

late_initcall(init_pm_wake_signal);
#endif