Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+65
View File
@@ -0,0 +1,65 @@
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux,intel}.com)
#
# Don't instrument UML-specific code; without this, we may crash when
# accessing the instrumentation buffer for the first time from the
# kernel.
KCOV_INSTRUMENT := n
CPPFLAGS_vmlinux.lds := -DSTART=$(LDS_START) \
-DELF_ARCH=$(LDS_ELF_ARCH) \
-DELF_FORMAT=$(LDS_ELF_FORMAT) \
$(LDS_EXTRA)
always-$(KBUILD_BUILTIN) := vmlinux.lds
obj-y = config.o exec.o exitcode.o irq.o ksyms.o mem.o \
physmem.o process.o ptrace.o reboot.o sigio.o \
signal.o sysrq.o time.o tlb.o trap.o \
um_arch.o umid.o kmsg_dump.o capflags.o skas/
obj-y += load_file.o
obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o
obj-$(CONFIG_GPROF) += gprof_syms.o
obj-$(CONFIG_OF) += dtb.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_SMP) += smp.o
USER_OBJS := config.o
include $(srctree)/arch/um/scripts/Makefile.rules
targets := config.c config.tmp capflags.c
# Be careful with the below Sed code - sed is pitfall-rich!
# We use sed to lower build requirements, for "embedded" builders for instance.
$(obj)/config.tmp: $(objtree)/.config FORCE
$(call if_changed,quote1)
quiet_cmd_quote1 = QUOTE $@
cmd_quote1 = sed -e 's/"/\\"/g' -e 's/^/"/' -e 's/$$/\\n",/' \
$< > $@
$(obj)/config.c: $(src)/config.c.in $(obj)/config.tmp FORCE
$(call if_changed,quote2)
quiet_cmd_mkcapflags = MKCAP $@
cmd_mkcapflags = $(CONFIG_SHELL) $(src)/../../x86/kernel/cpu/mkcapflags.sh $@ $^
cpufeature = $(src)/../../x86/include/asm/cpufeatures.h
vmxfeature = $(src)/../../x86/include/asm/vmxfeatures.h
$(obj)/capflags.c: $(cpufeature) $(vmxfeature) $(src)/../../x86/kernel/cpu/mkcapflags.sh FORCE
$(call if_changed,mkcapflags)
quiet_cmd_quote2 = QUOTE $@
cmd_quote2 = sed -e '/CONFIG/{' \
-e 's/"CONFIG"//' \
-e 'r $(obj)/config.tmp' \
-e 'a \' \
-e '""' \
-e '}' \
$< > $@
+49
View File
@@ -0,0 +1,49 @@
/* SPDX-License-Identifier: GPL-2.0 */
#define COMPILE_OFFSETS
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/elf.h>
#include <linux/crypto.h>
#include <linux/kbuild.h>
#include <linux/audit.h>
#include <linux/fs.h>
#include <asm/mman.h>
#include <asm/seccomp.h>
#include <asm/extable.h>
/* workaround for a warning with -Wmissing-prototypes */
void foo(void);
void foo(void)
{
DEFINE(KERNEL_MADV_REMOVE, MADV_REMOVE);
DEFINE(UM_KERN_PAGE_SIZE, PAGE_SIZE);
DEFINE(UM_KERN_PAGE_MASK, PAGE_MASK);
DEFINE(UM_KERN_PAGE_SHIFT, PAGE_SHIFT);
DEFINE(UM_GFP_KERNEL, GFP_KERNEL);
DEFINE(UM_GFP_ATOMIC, GFP_ATOMIC);
DEFINE(UM_THREAD_SIZE, THREAD_SIZE);
DEFINE(UM_NSEC_PER_SEC, NSEC_PER_SEC);
DEFINE(UM_NSEC_PER_USEC, NSEC_PER_USEC);
DEFINE(UM_KERN_GDT_ENTRY_TLS_ENTRIES, GDT_ENTRY_TLS_ENTRIES);
DEFINE(UM_SECCOMP_ARCH_NATIVE, SECCOMP_ARCH_NATIVE);
DEFINE(HOSTFS_ATTR_MODE, ATTR_MODE);
DEFINE(HOSTFS_ATTR_UID, ATTR_UID);
DEFINE(HOSTFS_ATTR_GID, ATTR_GID);
DEFINE(HOSTFS_ATTR_SIZE, ATTR_SIZE);
DEFINE(HOSTFS_ATTR_ATIME, ATTR_ATIME);
DEFINE(HOSTFS_ATTR_MTIME, ATTR_MTIME);
DEFINE(HOSTFS_ATTR_CTIME, ATTR_CTIME);
DEFINE(HOSTFS_ATTR_ATIME_SET, ATTR_ATIME_SET);
DEFINE(HOSTFS_ATTR_MTIME_SET, ATTR_MTIME_SET);
DEFINE(ALT_INSTR_SIZE, sizeof(struct alt_instr));
DEFINE(EXTABLE_SIZE, sizeof(struct exception_table_entry));
}
+26
View File
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
*/
#include <stdio.h>
#include <stdlib.h>
#include <init.h>
static __initdata const char *config[] = {
"CONFIG"
};
static int __init print_config(char *line, int *add)
{
int i;
for (i = 0; i < sizeof(config)/sizeof(config[0]); i++)
printf("%s", config[i]);
exit(0);
}
__uml_setup("--showconfig", print_config,
"--showconfig\n"
" Prints the config file that this UML binary was generated from.\n\n"
);
+42
View File
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/init.h>
#include <linux/of_fdt.h>
#include <linux/printk.h>
#include <linux/memblock.h>
#include <init.h>
#include "um_arch.h"
static char *dtb __initdata;
void uml_dtb_init(void)
{
long long size;
void *area;
area = uml_load_file(dtb, &size);
if (area) {
if (!early_init_dt_scan(area, __pa(area))) {
pr_err("invalid DTB %s\n", dtb);
memblock_free(area, size);
return;
}
early_init_fdt_scan_reserved_mem();
}
unflatten_device_tree();
}
static int __init uml_dtb_setup(char *line, int *add)
{
*add = 0;
dtb = line;
return 0;
}
__uml_setup("dtb=", uml_dtb_setup,
"dtb=<file>\n"
" Boot the kernel with the devicetree blob from the specified file.\n\n"
);
+182
View File
@@ -0,0 +1,182 @@
#include <asm/vmlinux.lds.h>
#include <asm/page.h>
OUTPUT_FORMAT(ELF_FORMAT)
OUTPUT_ARCH(ELF_ARCH)
ENTRY(_start)
jiffies = jiffies_64;
VERSION {
{
local: *;
};
}
SECTIONS
{
PROVIDE (__executable_start = START);
. = START + SIZEOF_HEADERS;
.interp : { *(.interp) }
__binary_start = .;
. = ALIGN(4096); /* Init code and data */
_text = .;
INIT_TEXT_SECTION(PAGE_SIZE)
. = ALIGN(PAGE_SIZE);
/* Read-only sections, merged into text segment: */
.hash : { *(.hash) }
.gnu.hash : { *(.gnu.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rel.init : { *(.rel.init) }
.rela.init : { *(.rela.init) }
.rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) }
.rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) }
.rel.fini : { *(.rel.fini) }
.rela.fini : { *(.rela.fini) }
.rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) }
.rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) }
.rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) }
.rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) }
.rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) }
.rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
.rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) }
.rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
.rel.ctors : { *(.rel.ctors) }
.rela.ctors : { *(.rela.ctors) }
.rel.dtors : { *(.rel.dtors) }
.rela.dtors : { *(.rela.dtors) }
.rel.got : { *(.rel.got) }
.rela.got : { *(.rela.got) }
.rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) }
.rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) }
.rel.plt : {
*(.rel.plt)
PROVIDE_HIDDEN(__rel_iplt_start = .);
*(.rel.iplt)
PROVIDE_HIDDEN(__rel_iplt_end = .);
}
.rela.plt : {
*(.rela.plt)
PROVIDE_HIDDEN(__rela_iplt_start = .);
*(.rela.iplt)
PROVIDE_HIDDEN(__rela_iplt_end = .);
}
.init : {
KEEP (*(.init))
} =0x90909090
.plt : { *(.plt) }
.text : {
_stext = .;
TEXT_TEXT
SCHED_TEXT
LOCK_TEXT
IRQENTRY_TEXT
SOFTIRQENTRY_TEXT
*(.fixup)
*(.stub .text.* .gnu.linkonce.t.*)
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
. = ALIGN(PAGE_SIZE);
} =0x90909090
. = ALIGN(PAGE_SIZE);
.syscall_stub : {
__syscall_stub_start = .;
*(.__syscall_stub*)
__syscall_stub_end = .;
}
.fini : {
KEEP (*(.fini))
} =0x90909090
.kstrtab : { *(.kstrtab) }
#include <asm/common.lds.S>
__init_begin = .;
init.data : { INIT_DATA }
__init_end = .;
/* Ensure the __preinit_array_start label is properly aligned. We
could instead move the label definition inside the section, but
the linker would then create the section even if it turns out to
be empty, which isn't pretty. */
. = ALIGN(32 / 8);
.preinit_array : { *(.preinit_array) }
.init_array : {
*(.kasan_init)
*(.init_array.*)
*(.init_array)
}
.fini_array : { *(.fini_array) }
.data : {
INIT_TASK_DATA(KERNEL_STACK_SIZE)
DATA_DATA
*(.data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
.data1 : { *(.data1) }
.tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
.eh_frame : { KEEP (*(.eh_frame)) }
.gcc_except_table : { *(.gcc_except_table) }
.dynamic : { *(.dynamic) }
.ctors : {
/* gcc uses crtbegin.o to find the start of
the constructors, so we make sure it is
first. Because this is a wildcard, it
doesn't matter if the user does not
actually link against crtbegin.o; the
linker won't look for a file to match a
wildcard. The wildcard also means that it
doesn't matter which directory crtbegin.o
is in. */
KEEP (*crtbegin.o(.ctors))
/* We don't want to include the .ctor section from
from the crtend.o file until after the sorted ctors.
The .ctor section from the crtend file contains the
end of ctors marker and it must be last */
KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
}
.dtors : {
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
}
.jcr : { KEEP (*(.jcr)) }
.got : { *(.got.plt) *(.got) }
_edata = .;
PROVIDE (edata = .);
.bss : {
__bss_start = .;
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections. */
. = ALIGN(32 / 8);
. = ALIGN(32 / 8);
}
__bss_stop = .;
_end = .;
PROVIDE (end = .);
STABS_DEBUG
DWARF_DEBUG
MODINFO
ELF_DETAILS
DISCARDS
}
ASSERT(__syscall_stub_end - __syscall_stub_start <= PAGE_SIZE,
"STUB code must not be larger than one page");
+32
View File
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 Richard Weinberger <richrd@nod.at>
*/
#include <linux/kernel.h>
#include <linux/console.h>
#include <linux/init.h>
#include <os.h>
static void early_console_write(struct console *con, const char *s, unsigned int n)
{
um_early_printk(s, n);
}
static struct console early_console_dev = {
.name = "earlycon",
.write = early_console_write,
.flags = CON_BOOT,
.index = -1,
};
static int __init setup_early_printk(char *buf)
{
if (!early_console) {
early_console = &early_console_dev;
register_console(&early_console_dev);
}
return 0;
}
early_param("earlyprintk", setup_early_printk);
+37
View File
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/stddef.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/ptrace.h>
#include <linux/sched/mm.h>
#include <linux/sched/task.h>
#include <linux/sched/task_stack.h>
#include <linux/slab.h>
#include <asm/current.h>
#include <asm/processor.h>
#include <linux/uaccess.h>
#include <as-layout.h>
#include <mem_user.h>
#include <registers.h>
#include <skas.h>
#include <os.h>
void flush_thread(void)
{
arch_flush_thread(&current->thread.arch);
get_safe_registers(current_pt_regs()->regs.gp,
current_pt_regs()->regs.fp);
}
void start_thread(struct pt_regs *regs, unsigned long eip, unsigned long esp)
{
PT_REGS_IP(regs) = eip;
PT_REGS_SP(regs) = esp;
clear_thread_flag(TIF_SINGLESTEP);
}
EXPORT_SYMBOL(start_thread);
+79
View File
@@ -0,0 +1,79 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/ctype.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/types.h>
#include <linux/uaccess.h>
/*
* If read and write race, the read will still atomically read a valid
* value.
*/
int uml_exitcode = 0;
static int exitcode_proc_show(struct seq_file *m, void *v)
{
int val;
/*
* Save uml_exitcode in a local so that we don't need to guarantee
* that sprintf accesses it atomically.
*/
val = uml_exitcode;
seq_printf(m, "%d\n", val);
return 0;
}
static int exitcode_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, exitcode_proc_show, NULL);
}
static ssize_t exitcode_proc_write(struct file *file,
const char __user *buffer, size_t count, loff_t *pos)
{
char *end, buf[sizeof("nnnnn\0")];
size_t size;
int tmp;
size = min(count, sizeof(buf));
if (copy_from_user(buf, buffer, size))
return -EFAULT;
tmp = simple_strtol(buf, &end, 0);
if ((*end != '\0') && !isspace(*end))
return -EINVAL;
uml_exitcode = tmp;
return count;
}
static const struct proc_ops exitcode_proc_ops = {
.proc_open = exitcode_proc_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
.proc_write = exitcode_proc_write,
};
static int make_proc_exitcode(void)
{
struct proc_dir_entry *ent;
ent = proc_create("exitcode", 0600, NULL, &exitcode_proc_ops);
if (ent == NULL) {
printk(KERN_WARNING "make_proc_exitcode : Failed to register "
"/proc/exitcode\n");
return 0;
}
return 0;
}
__initcall(make_proc_exitcode);
+9
View File
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/module.h>
extern void mcount(void);
EXPORT_SYMBOL(mcount);
+46
View File
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/init.h>
#include <linux/memblock.h>
#include <linux/initrd.h>
#include <asm/types.h>
#include <init.h>
#include <os.h>
#include "um_arch.h"
/* Changed by uml_initrd_setup, which is a setup */
static char *initrd __initdata = NULL;
int __init read_initrd(void)
{
unsigned long long size;
void *area;
if (!initrd)
return 0;
area = uml_load_file(initrd, &size);
if (!area)
return 0;
initrd_start = (unsigned long) area;
initrd_end = initrd_start + size;
return 0;
}
static int __init uml_initrd_setup(char *line, int *add)
{
*add = 0;
initrd = line;
return 0;
}
__uml_setup("initrd=", uml_initrd_setup,
"initrd=<initrd image>\n"
" This is used to boot UML from an initrd image. The argument is the\n"
" name of the file containing the image.\n\n"
);
+728
View File
@@ -0,0 +1,728 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2017 - Cambridge Greys Ltd
* Copyright (C) 2011 - 2014 Cisco Systems Inc
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Derived (i.e. mostly copied) from arch/i386/kernel/irq.c:
* Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar
*/
#include <linux/cpumask.h>
#include <linux/hardirq.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <as-layout.h>
#include <kern_util.h>
#include <os.h>
#include <irq_user.h>
#include <irq_kern.h>
#include <linux/time-internal.h>
DEFINE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);
#define irq_stats(x) (&per_cpu(irq_stat, x))
/* When epoll triggers we do not know why it did so
* we can also have different IRQs for read and write.
* This is why we keep a small irq_reg array for each fd -
* one entry per IRQ type
*/
struct irq_reg {
void *id;
int irq;
/* it's cheaper to store this than to query it */
int events;
bool active;
bool pending;
bool wakeup;
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
bool pending_event;
void (*timetravel_handler)(int, int, void *,
struct time_travel_event *);
struct time_travel_event event;
#endif
};
struct irq_entry {
struct list_head list;
int fd;
struct irq_reg reg[NUM_IRQ_TYPES];
bool suspended;
bool sigio_workaround;
};
static DEFINE_RAW_SPINLOCK(irq_lock);
static LIST_HEAD(active_fds);
static DECLARE_BITMAP(irqs_allocated, UM_LAST_SIGNAL_IRQ);
static bool irqs_suspended;
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
static bool irqs_pending;
#endif
static void irq_io_loop(struct irq_reg *irq, struct uml_pt_regs *regs)
{
/*
* irq->active guards against reentry
* irq->pending accumulates pending requests
* if pending is raised the irq_handler is re-run
* until pending is cleared
*/
if (irq->active) {
irq->active = false;
do {
irq->pending = false;
do_IRQ(irq->irq, regs);
} while (irq->pending);
irq->active = true;
} else {
irq->pending = true;
}
}
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
static void irq_event_handler(struct time_travel_event *ev)
{
struct irq_reg *reg = container_of(ev, struct irq_reg, event);
/* do nothing if suspended; just cause a wakeup and mark as pending */
if (irqs_suspended) {
irqs_pending = true;
reg->pending_event = true;
return;
}
generic_handle_irq(reg->irq);
}
static bool irq_do_timetravel_handler(struct irq_entry *entry,
enum um_irq_type t)
{
struct irq_reg *reg = &entry->reg[t];
if (!reg->timetravel_handler)
return false;
/*
* Handle all messages - we might get multiple even while
* interrupts are already suspended, due to suspend order
* etc. Note that time_travel_add_irq_event() will not add
* an event twice, if it's pending already "first wins".
*/
reg->timetravel_handler(reg->irq, entry->fd, reg->id, &reg->event);
if (!reg->event.pending)
return false;
return true;
}
static void irq_do_pending_events(bool timetravel_handlers_only)
{
struct irq_entry *entry;
if (!irqs_pending || timetravel_handlers_only)
return;
irqs_pending = false;
list_for_each_entry(entry, &active_fds, list) {
enum um_irq_type t;
for (t = 0; t < NUM_IRQ_TYPES; t++) {
struct irq_reg *reg = &entry->reg[t];
/*
* Any timetravel_handler was invoked already, just
* directly run the IRQ.
*/
if (reg->pending_event) {
irq_enter();
generic_handle_irq(reg->irq);
irq_exit();
reg->pending_event = false;
}
}
}
}
#else
static bool irq_do_timetravel_handler(struct irq_entry *entry,
enum um_irq_type t)
{
return false;
}
static void irq_do_pending_events(bool timetravel_handlers_only)
{
}
#endif
static void sigio_reg_handler(int idx, struct irq_entry *entry, enum um_irq_type t,
struct uml_pt_regs *regs,
bool timetravel_handlers_only)
{
struct irq_reg *reg = &entry->reg[t];
if (!reg->events)
return;
if (os_epoll_triggered(idx, reg->events) <= 0)
return;
if (irq_do_timetravel_handler(entry, t))
return;
/*
* If we're called to only run time-travel handlers then don't
* actually proceed but mark sigio as pending (if applicable).
* For suspend/resume, timetravel_handlers_only may be true
* despite time-travel not being configured and used.
*/
if (timetravel_handlers_only) {
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
reg->pending_event = true;
irqs_pending = true;
mark_sigio_pending();
#endif
return;
}
irq_io_loop(reg, regs);
}
static void _sigio_handler(struct uml_pt_regs *regs,
bool timetravel_handlers_only)
{
struct irq_entry *irq_entry;
int n, i;
if (timetravel_handlers_only && !um_irq_timetravel_handler_used())
return;
/* Flush out pending events that were ignored due to time-travel. */
if (!irqs_suspended)
irq_do_pending_events(timetravel_handlers_only);
while (1) {
/* This is now lockless - epoll keeps back-referencesto the irqs
* which have trigger it so there is no need to walk the irq
* list and lock it every time. We avoid locking by turning off
* IO for a specific fd by executing os_del_epoll_fd(fd) before
* we do any changes to the actual data structures
*/
n = os_waiting_for_events_epoll();
if (n <= 0) {
if (n == -EINTR)
continue;
else
break;
}
for (i = 0; i < n ; i++) {
enum um_irq_type t;
irq_entry = os_epoll_get_data_pointer(i);
for (t = 0; t < NUM_IRQ_TYPES; t++)
sigio_reg_handler(i, irq_entry, t, regs,
timetravel_handlers_only);
}
}
if (!timetravel_handlers_only)
free_irqs();
}
void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs,
void *mc)
{
preempt_disable();
_sigio_handler(regs, irqs_suspended);
preempt_enable();
}
static struct irq_entry *get_irq_entry_by_fd(int fd)
{
struct irq_entry *walk;
lockdep_assert_held(&irq_lock);
list_for_each_entry(walk, &active_fds, list) {
if (walk->fd == fd)
return walk;
}
return NULL;
}
static void remove_irq_entry(struct irq_entry *to_free, bool remove)
{
if (!to_free)
return;
if (remove)
os_del_epoll_fd(to_free->fd);
list_del(&to_free->list);
}
static bool update_irq_entry(struct irq_entry *entry)
{
enum um_irq_type i;
int events = 0;
for (i = 0; i < NUM_IRQ_TYPES; i++)
events |= entry->reg[i].events;
if (events) {
/* will modify (instead of add) if needed */
os_add_epoll_fd(events, entry->fd, entry);
return true;
}
os_del_epoll_fd(entry->fd);
return false;
}
static struct irq_entry *update_or_remove_irq_entry(struct irq_entry *entry)
{
if (update_irq_entry(entry))
return NULL;
remove_irq_entry(entry, false);
return entry;
}
static int activate_fd(int irq, int fd, enum um_irq_type type, void *dev_id,
void (*timetravel_handler)(int, int, void *,
struct time_travel_event *))
{
struct irq_entry *irq_entry, *to_free = NULL;
int err, events = os_event_mask(type);
unsigned long flags;
err = os_set_fd_async(fd);
if (err < 0)
goto out;
raw_spin_lock_irqsave(&irq_lock, flags);
irq_entry = get_irq_entry_by_fd(fd);
if (irq_entry) {
already:
/* cannot register the same FD twice with the same type */
if (WARN_ON(irq_entry->reg[type].events)) {
err = -EALREADY;
goto out_unlock;
}
/* temporarily disable to avoid IRQ-side locking */
os_del_epoll_fd(fd);
} else {
struct irq_entry *new;
/* don't restore interrupts */
raw_spin_unlock(&irq_lock);
new = kzalloc_obj(*irq_entry, GFP_ATOMIC);
if (!new) {
local_irq_restore(flags);
return -ENOMEM;
}
raw_spin_lock(&irq_lock);
irq_entry = get_irq_entry_by_fd(fd);
if (irq_entry) {
to_free = new;
goto already;
}
irq_entry = new;
irq_entry->fd = fd;
list_add_tail(&irq_entry->list, &active_fds);
maybe_sigio_broken(fd);
}
irq_entry->reg[type].id = dev_id;
irq_entry->reg[type].irq = irq;
irq_entry->reg[type].active = true;
irq_entry->reg[type].events = events;
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
if (um_irq_timetravel_handler_used()) {
irq_entry->reg[type].timetravel_handler = timetravel_handler;
irq_entry->reg[type].event.fn = irq_event_handler;
}
#endif
WARN_ON(!update_irq_entry(irq_entry));
err = 0;
out_unlock:
raw_spin_unlock_irqrestore(&irq_lock, flags);
out:
kfree(to_free);
return err;
}
/*
* Remove the entry or entries for a specific FD, if you
* don't want to remove all the possible entries then use
* um_free_irq() or deactivate_fd() instead.
*/
void free_irq_by_fd(int fd)
{
struct irq_entry *to_free;
unsigned long flags;
raw_spin_lock_irqsave(&irq_lock, flags);
to_free = get_irq_entry_by_fd(fd);
remove_irq_entry(to_free, true);
raw_spin_unlock_irqrestore(&irq_lock, flags);
kfree(to_free);
}
EXPORT_SYMBOL(free_irq_by_fd);
static void free_irq_by_irq_and_dev(unsigned int irq, void *dev)
{
struct irq_entry *entry, *to_free = NULL;
unsigned long flags;
raw_spin_lock_irqsave(&irq_lock, flags);
list_for_each_entry(entry, &active_fds, list) {
enum um_irq_type i;
for (i = 0; i < NUM_IRQ_TYPES; i++) {
struct irq_reg *reg = &entry->reg[i];
if (!reg->events)
continue;
if (reg->irq != irq)
continue;
if (reg->id != dev)
continue;
os_del_epoll_fd(entry->fd);
reg->events = 0;
to_free = update_or_remove_irq_entry(entry);
goto out;
}
}
out:
raw_spin_unlock_irqrestore(&irq_lock, flags);
kfree(to_free);
}
void deactivate_fd(int fd, int irqnum)
{
struct irq_entry *entry;
unsigned long flags;
enum um_irq_type i;
os_del_epoll_fd(fd);
raw_spin_lock_irqsave(&irq_lock, flags);
entry = get_irq_entry_by_fd(fd);
if (!entry)
goto out;
for (i = 0; i < NUM_IRQ_TYPES; i++) {
if (!entry->reg[i].events)
continue;
if (entry->reg[i].irq == irqnum)
entry->reg[i].events = 0;
}
entry = update_or_remove_irq_entry(entry);
out:
raw_spin_unlock_irqrestore(&irq_lock, flags);
kfree(entry);
ignore_sigio_fd(fd);
}
EXPORT_SYMBOL(deactivate_fd);
/*
* Called just before shutdown in order to provide a clean exec
* environment in case the system is rebooting. No locking because
* that would cause a pointless shutdown hang if something hadn't
* released the lock.
*/
int deactivate_all_fds(void)
{
struct irq_entry *entry;
/* Stop IO. The IRQ loop has no lock so this is our
* only way of making sure we are safe to dispose
* of all IRQ handlers
*/
os_set_ioignore();
/* we can no longer call kfree() here so just deactivate */
list_for_each_entry(entry, &active_fds, list)
os_del_epoll_fd(entry->fd);
os_close_epoll_fd();
return 0;
}
/*
* do_IRQ handles all normal device IRQs (the special
* SMP cross-CPU interrupts have their own specific
* handlers).
*/
unsigned int do_IRQ(int irq, struct uml_pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs((struct pt_regs *)regs);
irq_enter();
generic_handle_irq(irq);
irq_exit();
set_irq_regs(old_regs);
return 1;
}
void um_free_irq(int irq, void *dev)
{
if (WARN(irq < 0 || irq > UM_LAST_SIGNAL_IRQ,
"freeing invalid irq %d", irq))
return;
free_irq_by_irq_and_dev(irq, dev);
free_irq(irq, dev);
clear_bit(irq, irqs_allocated);
}
EXPORT_SYMBOL(um_free_irq);
static int
_um_request_irq(int irq, int fd, enum um_irq_type type,
irq_handler_t handler, unsigned long irqflags,
const char *devname, void *dev_id,
void (*timetravel_handler)(int, int, void *,
struct time_travel_event *))
{
int err;
if (irq == UM_IRQ_ALLOC) {
int i;
for (i = UM_FIRST_DYN_IRQ; i < NR_IRQS; i++) {
if (!test_and_set_bit(i, irqs_allocated)) {
irq = i;
break;
}
}
}
if (irq < 0)
return -ENOSPC;
if (fd != -1) {
err = activate_fd(irq, fd, type, dev_id, timetravel_handler);
if (err)
goto error;
}
err = request_irq(irq, handler, irqflags, devname, dev_id);
if (err < 0)
goto error;
return irq;
error:
clear_bit(irq, irqs_allocated);
return err;
}
int um_request_irq(int irq, int fd, enum um_irq_type type,
irq_handler_t handler, unsigned long irqflags,
const char *devname, void *dev_id)
{
return _um_request_irq(irq, fd, type, handler, irqflags,
devname, dev_id, NULL);
}
EXPORT_SYMBOL(um_request_irq);
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
int um_request_irq_tt(int irq, int fd, enum um_irq_type type,
irq_handler_t handler, unsigned long irqflags,
const char *devname, void *dev_id,
void (*timetravel_handler)(int, int, void *,
struct time_travel_event *))
{
return _um_request_irq(irq, fd, type, handler, irqflags,
devname, dev_id, timetravel_handler);
}
EXPORT_SYMBOL(um_request_irq_tt);
void sigio_run_timetravel_handlers(void)
{
_sigio_handler(NULL, true);
}
#endif
#ifdef CONFIG_PM_SLEEP
void um_irqs_suspend(void)
{
struct irq_entry *entry;
unsigned long flags;
irqs_suspended = true;
raw_spin_lock_irqsave(&irq_lock, flags);
list_for_each_entry(entry, &active_fds, list) {
enum um_irq_type t;
bool clear = true;
for (t = 0; t < NUM_IRQ_TYPES; t++) {
if (!entry->reg[t].events)
continue;
/*
* For the SIGIO_WRITE_IRQ, which is used to handle the
* SIGIO workaround thread, we need special handling:
* enable wake for it itself, but below we tell it about
* any FDs that should be suspended.
*/
if (entry->reg[t].wakeup ||
entry->reg[t].irq == SIGIO_WRITE_IRQ
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
|| entry->reg[t].timetravel_handler
#endif
) {
clear = false;
break;
}
}
if (clear) {
entry->suspended = true;
os_clear_fd_async(entry->fd);
entry->sigio_workaround =
!__ignore_sigio_fd(entry->fd);
}
}
raw_spin_unlock_irqrestore(&irq_lock, flags);
}
void um_irqs_resume(void)
{
struct irq_entry *entry;
unsigned long flags;
raw_spin_lock_irqsave(&irq_lock, flags);
list_for_each_entry(entry, &active_fds, list) {
if (entry->suspended) {
int err = os_set_fd_async(entry->fd);
WARN(err < 0, "os_set_fd_async returned %d\n", err);
entry->suspended = false;
if (entry->sigio_workaround) {
err = __add_sigio_fd(entry->fd);
WARN(err < 0, "add_sigio_returned %d\n", err);
}
}
}
raw_spin_unlock_irqrestore(&irq_lock, flags);
irqs_suspended = false;
send_sigio_to_self();
}
static int normal_irq_set_wake(struct irq_data *d, unsigned int on)
{
struct irq_entry *entry;
unsigned long flags;
raw_spin_lock_irqsave(&irq_lock, flags);
list_for_each_entry(entry, &active_fds, list) {
enum um_irq_type t;
for (t = 0; t < NUM_IRQ_TYPES; t++) {
if (!entry->reg[t].events)
continue;
if (entry->reg[t].irq != d->irq)
continue;
entry->reg[t].wakeup = on;
goto unlock;
}
}
unlock:
raw_spin_unlock_irqrestore(&irq_lock, flags);
return 0;
}
#else
#define normal_irq_set_wake NULL
#endif
/*
* irq_chip must define at least enable/disable and ack when
* the edge handler is used.
*/
static void dummy(struct irq_data *d)
{
}
/* This is used for everything other than the timer. */
static struct irq_chip normal_irq_type = {
.name = "SIGIO",
.irq_disable = dummy,
.irq_enable = dummy,
.irq_ack = dummy,
.irq_mask = dummy,
.irq_unmask = dummy,
.irq_set_wake = normal_irq_set_wake,
};
static struct irq_chip alarm_irq_type = {
.name = "SIGALRM",
.irq_disable = dummy,
.irq_enable = dummy,
.irq_ack = dummy,
.irq_mask = dummy,
.irq_unmask = dummy,
};
void __init init_IRQ(void)
{
int i;
irq_set_chip_and_handler(TIMER_IRQ, &alarm_irq_type, handle_percpu_irq);
for (i = 1; i < UM_LAST_SIGNAL_IRQ; i++)
irq_set_chip_and_handler(i, &normal_irq_type, handle_edge_irq);
/* Initialize EPOLL Loop */
os_setup_epoll();
}
int __init arch_probe_nr_irqs(void)
{
return NR_IRQS;
}
void sigchld_handler(int sig, struct siginfo *unused_si,
struct uml_pt_regs *regs, void *mc)
{
do_IRQ(SIGCHLD_IRQ, regs);
}
/*
* /proc/interrupts printing for arch specific interrupts
*/
int arch_show_interrupts(struct seq_file *p, int prec)
{
#if IS_ENABLED(CONFIG_SMP)
int cpu;
seq_printf(p, "%*s: ", prec, "RES");
for_each_online_cpu(cpu)
seq_printf(p, "%10u ", irq_stats(cpu)->irq_resched_count);
seq_puts(p, " Rescheduling interrupts\n");
seq_printf(p, "%*s: ", prec, "CAL");
for_each_online_cpu(cpu)
seq_printf(p, "%10u ", irq_stats(cpu)->irq_call_count);
seq_puts(p, " Function call interrupts\n");
#endif
return 0;
}
+65
View File
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/kmsg_dump.h>
#include <linux/spinlock.h>
#include <linux/console.h>
#include <linux/string.h>
#include <shared/init.h>
#include <shared/kern.h>
#include <os.h>
static void kmsg_dumper_stdout(struct kmsg_dumper *dumper,
struct kmsg_dump_detail *detail)
{
static struct kmsg_dump_iter iter;
static DEFINE_SPINLOCK(lock);
static char line[1024];
struct console *con;
unsigned long flags;
size_t len = 0;
int cookie;
/*
* If no consoles are available to output crash information, dump
* the kmsg buffer to stdout.
*/
cookie = console_srcu_read_lock();
for_each_console_srcu(con) {
/*
* The ttynull console and disabled consoles are ignored
* since they cannot output. All other consoles are
* expected to output the crash information.
*/
if (strcmp(con->name, "ttynull") != 0 &&
console_is_usable(con, console_srcu_read_flags(con), true)) {
break;
}
}
console_srcu_read_unlock(cookie);
if (con)
return;
if (!spin_trylock_irqsave(&lock, flags))
return;
kmsg_dump_rewind(&iter);
printf("kmsg_dump:\n");
while (kmsg_dump_get_line(&iter, true, line, sizeof(line), &len)) {
line[len] = '\0';
printf("%s", line);
}
spin_unlock_irqrestore(&lock, flags);
}
static struct kmsg_dumper kmsg_dumper = {
.dump = kmsg_dumper_stdout
};
static int __init kmsg_dumper_stdout_init(void)
{
return kmsg_dump_register(&kmsg_dumper);
}
__uml_postsetup(kmsg_dumper_stdout_init);
+48
View File
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/module.h>
#include <os.h>
EXPORT_SYMBOL(um_get_signals);
EXPORT_SYMBOL(um_set_signals);
EXPORT_SYMBOL(os_stat_fd);
EXPORT_SYMBOL(os_stat_file);
EXPORT_SYMBOL(os_access);
EXPORT_SYMBOL(os_set_exec_close);
EXPORT_SYMBOL(os_getpid);
EXPORT_SYMBOL(os_open_file);
EXPORT_SYMBOL(os_read_file);
EXPORT_SYMBOL(os_write_file);
EXPORT_SYMBOL(os_seek_file);
EXPORT_SYMBOL(os_lock_file);
EXPORT_SYMBOL(os_ioctl_generic);
EXPORT_SYMBOL(os_pipe);
EXPORT_SYMBOL(os_file_type);
EXPORT_SYMBOL(os_file_mode);
EXPORT_SYMBOL(os_file_size);
EXPORT_SYMBOL(os_flush_stdout);
EXPORT_SYMBOL(os_close_file);
EXPORT_SYMBOL(os_set_fd_async);
EXPORT_SYMBOL(os_set_fd_block);
EXPORT_SYMBOL(helper_wait);
EXPORT_SYMBOL(os_shutdown_socket);
EXPORT_SYMBOL(os_create_unix_socket);
EXPORT_SYMBOL(os_connect_socket);
EXPORT_SYMBOL(os_accept_connection);
EXPORT_SYMBOL(os_rcv_fd_msg);
EXPORT_SYMBOL(run_helper);
EXPORT_SYMBOL(os_major);
EXPORT_SYMBOL(os_minor);
EXPORT_SYMBOL(os_makedev);
EXPORT_SYMBOL(os_eventfd);
EXPORT_SYMBOL(os_sendmsg_fds);
EXPORT_SYMBOL(add_sigio_fd);
EXPORT_SYMBOL(ignore_sigio_fd);
EXPORT_SYMBOL(sigio_broken);
EXPORT_SYMBOL(syscall);
+59
View File
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/memblock.h>
#include <os.h>
#include "um_arch.h"
static int __init __uml_load_file(const char *filename, void *buf, int size)
{
int fd, n;
fd = os_open_file(filename, of_read(OPENFLAGS()), 0);
if (fd < 0) {
printk(KERN_ERR "Opening '%s' failed - err = %d\n", filename,
-fd);
return -1;
}
n = os_read_file(fd, buf, size);
if (n != size) {
printk(KERN_ERR "Read of %d bytes from '%s' failed, "
"err = %d\n", size,
filename, -n);
return -1;
}
os_close_file(fd);
return 0;
}
void *uml_load_file(const char *filename, unsigned long long *size)
{
void *area;
int err;
*size = 0;
if (!filename)
return NULL;
err = os_file_size(filename, size);
if (err)
return NULL;
if (*size == 0) {
printk(KERN_ERR "\"%s\" is empty\n", filename);
return NULL;
}
area = memblock_alloc_or_panic(*size, SMP_CACHE_BYTES);
if (__uml_load_file(filename, area, *size)) {
memblock_free(area, *size);
return NULL;
}
return area;
}
+139
View File
@@ -0,0 +1,139 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/stddef.h>
#include <linux/module.h>
#include <linux/memblock.h>
#include <linux/mm.h>
#include <linux/swap.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <asm/sections.h>
#include <asm/page.h>
#include <asm/pgalloc.h>
#include <as-layout.h>
#include <init.h>
#include <kern.h>
#include <kern_util.h>
#include <mem_user.h>
#include <os.h>
#include <um_malloc.h>
#include <linux/sched/task.h>
#include <linux/kasan.h>
#ifdef CONFIG_KASAN
void __init kasan_init(void)
{
/*
* kasan_map_memory will map all of the required address space and
* the host machine will allocate physical memory as necessary.
*/
kasan_map_memory((void *)KASAN_SHADOW_START, KASAN_SHADOW_SIZE);
init_task.kasan_depth = 0;
/*
* Since kasan_init() is called before main(),
* KASAN is initialized but the enablement is deferred after
* jump_label_init(). See arch_mm_preinit().
*/
}
static void (*kasan_init_ptr)(void)
__section(".kasan_init") __used
= kasan_init;
#endif
/*
* Initialized during boot, and readonly for initializing page tables
* afterwards
*/
pgd_t swapper_pg_dir[PTRS_PER_PGD];
/* Initialized at boot time, and readonly after that */
int kmalloc_ok = 0;
/* Used during early boot */
static unsigned long brk_end;
void __init arch_mm_preinit(void)
{
/* Safe to call after jump_label_init(). Enables KASAN. */
kasan_init_generic();
/* Map in the area just after the brk now that kmalloc is about
* to be turned on.
*/
brk_end = PAGE_ALIGN((unsigned long) sbrk(0));
map_memory(brk_end, __pa(brk_end), uml_reserved - brk_end, 1, 1, 0);
memblock_free((void *)brk_end, uml_reserved - brk_end);
uml_reserved = brk_end;
min_low_pfn = PFN_UP(__pa(uml_reserved));
max_pfn = max_low_pfn;
}
void __init mem_init(void)
{
kmalloc_ok = 1;
}
void __init arch_zone_limits_init(unsigned long *max_zone_pfns)
{
max_zone_pfns[ZONE_NORMAL] = high_physmem >> PAGE_SHIFT;
}
/*
* This can't do anything because nothing in the kernel image can be freed
* since it's not in kernel physical memory.
*/
void free_initmem(void)
{
}
/* Allocate and free page tables. */
pgd_t *pgd_alloc(struct mm_struct *mm)
{
pgd_t *pgd = __pgd_alloc(mm, 0);
if (pgd)
memcpy(pgd + USER_PTRS_PER_PGD,
swapper_pg_dir + USER_PTRS_PER_PGD,
(PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
return pgd;
}
void *uml_kmalloc(int size, int flags)
{
return kmalloc(size, flags);
}
static const pgprot_t protection_map[16] = {
[VM_NONE] = PAGE_NONE,
[VM_READ] = PAGE_READONLY,
[VM_WRITE] = PAGE_COPY,
[VM_WRITE | VM_READ] = PAGE_COPY,
[VM_EXEC] = PAGE_READONLY,
[VM_EXEC | VM_READ] = PAGE_READONLY,
[VM_EXEC | VM_WRITE] = PAGE_COPY,
[VM_EXEC | VM_WRITE | VM_READ] = PAGE_COPY,
[VM_SHARED] = PAGE_NONE,
[VM_SHARED | VM_READ] = PAGE_READONLY,
[VM_SHARED | VM_WRITE] = PAGE_SHARED,
[VM_SHARED | VM_WRITE | VM_READ] = PAGE_SHARED,
[VM_SHARED | VM_EXEC] = PAGE_READONLY,
[VM_SHARED | VM_EXEC | VM_READ] = PAGE_READONLY,
[VM_SHARED | VM_EXEC | VM_WRITE] = PAGE_SHARED,
[VM_SHARED | VM_EXEC | VM_WRITE | VM_READ] = PAGE_SHARED
};
DECLARE_VM_GET_PAGE_PROT
void mark_rodata_ro(void)
{
unsigned long rodata_start = PFN_ALIGN(__start_rodata);
unsigned long rodata_end = PFN_ALIGN(__end_rodata);
os_protect_memory((void *)rodata_start, rodata_end - rodata_start, 1, 0, 0);
}
+129
View File
@@ -0,0 +1,129 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/module.h>
#include <linux/memblock.h>
#include <linux/mm.h>
#include <linux/pfn.h>
#include <asm/page.h>
#include <asm/sections.h>
#include <as-layout.h>
#include <init.h>
#include <kern.h>
#include <kern_util.h>
#include <mem_user.h>
#include <os.h>
static int physmem_fd = -1;
/* Changed during early boot */
unsigned long high_physmem;
EXPORT_SYMBOL(high_physmem);
void map_memory(unsigned long virt, unsigned long phys, unsigned long len,
int r, int w, int x)
{
__u64 offset;
int fd, err;
fd = phys_mapping(phys, &offset);
err = os_map_memory((void *) virt, fd, offset, len, r, w, x);
if (err) {
if (err == -ENOMEM)
printk(KERN_ERR "try increasing the host's "
"/proc/sys/vm/max_map_count to <physical "
"memory size>/4096\n");
panic("map_memory(0x%lx, %d, 0x%llx, %ld, %d, %d, %d) failed, "
"err = %d\n", virt, fd, offset, len, r, w, x, err);
}
}
/**
* setup_physmem() - Setup physical memory for UML
* @start: Start address of the physical kernel memory,
* i.e start address of the executable image.
* @reserve_end: end address of the physical kernel memory.
* @len: Length of total physical memory that should be mapped/made
* available, in bytes.
*
* Creates an unlinked temporary file of size (len) and memory maps
* it on the last executable image address (uml_reserved).
*
* The offset is needed as the length of the total physical memory
* (len) includes the size of the memory used be the executable image,
* but the mapped-to address is the last address of the executable image
* (uml_reserved == end address of executable image).
*
* The memory mapped memory of the temporary file is used as backing memory
* of all user space processes/kernel tasks.
*/
void __init setup_physmem(unsigned long start, unsigned long reserve_end,
unsigned long len)
{
unsigned long reserve = reserve_end - start;
unsigned long map_size = len - reserve;
int err;
if (len <= reserve) {
os_warn("Too few physical memory! Needed=%lu, given=%lu\n",
reserve, len);
exit(1);
}
physmem_fd = create_mem_file(len);
err = os_map_memory((void *) reserve_end, physmem_fd, reserve,
map_size, 1, 1, 1);
if (err < 0) {
os_warn("setup_physmem - mapping %lu bytes of memory at 0x%p "
"failed - errno = %d\n", map_size,
(void *) reserve_end, err);
exit(1);
}
/*
* Special kludge - This page will be mapped in to userspace processes
* from physmem_fd, so it needs to be written out there.
*/
os_seek_file(physmem_fd, __pa(__syscall_stub_start));
os_write_file(physmem_fd, __syscall_stub_start, PAGE_SIZE);
memblock_add(__pa(start), len);
memblock_reserve(__pa(start), reserve);
min_low_pfn = PFN_UP(__pa(reserve_end));
max_low_pfn = min_low_pfn + (map_size >> PAGE_SHIFT);
}
int phys_mapping(unsigned long phys, unsigned long long *offset_out)
{
int fd = -1;
if (phys < physmem_size) {
fd = physmem_fd;
*offset_out = phys;
}
return fd;
}
EXPORT_SYMBOL(phys_mapping);
static int __init uml_mem_setup(char *line, int *add)
{
char *retptr;
*add = 0;
physmem_size = memparse(line,&retptr);
return 0;
}
__uml_setup("mem=", uml_mem_setup,
"mem=<Amount of desired ram>\n"
" This controls how much \"physical\" memory the kernel allocates\n"
" for the system. The size is specified as a number followed by\n"
" one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n"
" This is not related to the amount of memory in the host. It can\n"
" be more, and the excess, if it's ever used, will just be swapped out.\n"
" Example: mem=64M\n\n"
);
+309
View File
@@ -0,0 +1,309 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2015 Anton Ivanov (aivanov@{brocade.com,kot-begemot.co.uk})
* Copyright (C) 2015 Thomas Meyer (thomas@m3y3r.de)
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Copyright 2003 PathScale, Inc.
*/
#include <linux/stddef.h>
#include <linux/err.h>
#include <linux/hardirq.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/personality.h>
#include <linux/proc_fs.h>
#include <linux/ptrace.h>
#include <linux/random.h>
#include <linux/cpu.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/sched/debug.h>
#include <linux/sched/task.h>
#include <linux/sched/task_stack.h>
#include <linux/seq_file.h>
#include <linux/tick.h>
#include <linux/threads.h>
#include <linux/resume_user_mode.h>
#include <asm/current.h>
#include <asm/mmu_context.h>
#include <asm/switch_to.h>
#include <asm/exec.h>
#include <linux/uaccess.h>
#include <as-layout.h>
#include <kern_util.h>
#include <os.h>
#include <skas.h>
#include <registers.h>
#include <linux/time-internal.h>
#include <linux/elfcore.h>
/*
* This is a per-cpu array. A processor only modifies its entry and it only
* cares about its entry, so it's OK if another processor is modifying its
* entry.
*/
struct task_struct *cpu_tasks[NR_CPUS] = {
[0 ... NR_CPUS - 1] = &init_task,
};
EXPORT_SYMBOL(cpu_tasks);
void free_stack(unsigned long stack, int order)
{
free_pages(stack, order);
}
unsigned long alloc_stack(int order, int atomic)
{
unsigned long page;
gfp_t flags = GFP_KERNEL;
if (atomic)
flags = GFP_ATOMIC;
page = __get_free_pages(flags, order);
return page;
}
static inline void set_current(struct task_struct *task)
{
cpu_tasks[task_thread_info(task)->cpu] = task;
}
struct task_struct *__switch_to(struct task_struct *from, struct task_struct *to)
{
to->thread.prev_sched = from;
set_current(to);
switch_threads(&from->thread.switch_buf, &to->thread.switch_buf);
arch_switch_to(current);
return current->thread.prev_sched;
}
void interrupt_end(void)
{
struct pt_regs *regs = &current->thread.regs;
unsigned long thread_flags;
thread_flags = read_thread_flags();
while (thread_flags & _TIF_WORK_MASK) {
if (thread_flags & _TIF_NEED_RESCHED)
schedule();
if (thread_flags & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL))
do_signal(regs);
if (thread_flags & _TIF_NOTIFY_RESUME)
resume_user_mode_work(regs);
thread_flags = read_thread_flags();
}
}
int get_current_pid(void)
{
return task_pid_nr(current);
}
/*
* This is called magically, by its address being stuffed in a jmp_buf
* and being longjmp-d to.
*/
void new_thread_handler(void)
{
int (*fn)(void *);
void *arg;
if (current->thread.prev_sched != NULL)
schedule_tail(current->thread.prev_sched);
current->thread.prev_sched = NULL;
fn = current->thread.request.thread.proc;
arg = current->thread.request.thread.arg;
/*
* callback returns only if the kernel thread execs a process
*/
fn(arg);
userspace(&current->thread.regs.regs);
}
/* Called magically, see new_thread_handler above */
static void fork_handler(void)
{
schedule_tail(current->thread.prev_sched);
/*
* XXX: if interrupt_end() calls schedule, this call to
* arch_switch_to isn't needed. We could want to apply this to
* improve performance. -bb
*/
arch_switch_to(current);
current->thread.prev_sched = NULL;
userspace(&current->thread.regs.regs);
}
int copy_thread(struct task_struct * p, const struct kernel_clone_args *args)
{
u64 clone_flags = args->flags;
unsigned long sp = args->stack;
unsigned long tls = args->tls;
void (*handler)(void);
int ret = 0;
p->thread = (struct thread_struct) INIT_THREAD;
if (!args->fn) {
memcpy(&p->thread.regs.regs, current_pt_regs(),
sizeof(p->thread.regs.regs));
PT_REGS_SET_SYSCALL_RETURN(&p->thread.regs, 0);
if (sp != 0)
REGS_SP(p->thread.regs.regs.gp) = sp;
handler = fork_handler;
arch_copy_thread(&current->thread.arch, &p->thread.arch);
} else {
get_safe_registers(p->thread.regs.regs.gp, p->thread.regs.regs.fp);
p->thread.request.thread.proc = args->fn;
p->thread.request.thread.arg = args->fn_arg;
handler = new_thread_handler;
}
new_thread(task_stack_page(p), &p->thread.switch_buf, handler);
if (!args->fn) {
clear_flushed_tls(p);
/*
* Set a new TLS for the child thread?
*/
if (clone_flags & CLONE_SETTLS)
ret = arch_set_tls(p, tls);
}
return ret;
}
void initial_thread_cb(void (*proc)(void *), void *arg)
{
initial_thread_cb_skas(proc, arg);
}
int arch_dup_task_struct(struct task_struct *dst,
struct task_struct *src)
{
/* init_task is not dynamically sized (missing FPU state) */
if (unlikely(src == &init_task)) {
memcpy(dst, src, sizeof(init_task));
memset((void *)dst + sizeof(init_task), 0,
arch_task_struct_size - sizeof(init_task));
} else {
memcpy(dst, src, arch_task_struct_size);
}
return 0;
}
void um_idle_sleep(void)
{
if (time_travel_mode != TT_MODE_OFF)
time_travel_sleep();
else
os_idle_sleep();
}
void arch_cpu_idle(void)
{
um_idle_sleep();
}
void arch_cpu_idle_prepare(void)
{
os_idle_prepare();
}
int __uml_cant_sleep(void) {
return in_atomic() || irqs_disabled() || in_interrupt();
/* Is in_interrupt() really needed? */
}
int uml_need_resched(void)
{
return need_resched();
}
extern exitcall_t __uml_exitcall_begin, __uml_exitcall_end;
void do_uml_exitcalls(void)
{
exitcall_t *call;
call = &__uml_exitcall_end;
while (--call >= &__uml_exitcall_begin)
(*call)();
}
char *uml_strdup(const char *string)
{
return kstrdup(string, GFP_KERNEL);
}
EXPORT_SYMBOL(uml_strdup);
int copy_from_user_proc(void *to, void __user *from, int size)
{
return copy_from_user(to, from, size);
}
int singlestepping(void)
{
return test_thread_flag(TIF_SINGLESTEP);
}
/*
* Only x86 and x86_64 have an arch_align_stack().
* All other arches have "#define arch_align_stack(x) (x)"
* in their asm/exec.h
* As this is included in UML from asm-um/system-generic.h,
* we can use it to behave as the subarch does.
*/
#ifndef arch_align_stack
unsigned long arch_align_stack(unsigned long sp)
{
if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
sp -= get_random_u32_below(8192);
return sp & ~0xf;
}
#endif
unsigned long __get_wchan(struct task_struct *p)
{
unsigned long stack_page, sp, ip;
bool seen_sched = 0;
stack_page = (unsigned long) task_stack_page(p);
/* Bail if the process has no kernel stack for some reason */
if (stack_page == 0)
return 0;
sp = p->thread.switch_buf->JB_SP;
/*
* Bail if the stack pointer is below the bottom of the kernel
* stack for some reason
*/
if (sp < stack_page)
return 0;
while (sp < stack_page + THREAD_SIZE) {
ip = *((unsigned long *) sp);
if (in_sched_functions(ip))
/* Ignore everything until we're above the scheduler */
seen_sched = 1;
else if (kernel_text_address(ip) && seen_sched)
return ip;
sp += sizeof(unsigned long);
}
return 0;
}
+161
View File
@@ -0,0 +1,161 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/audit.h>
#include <linux/ptrace.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <asm/ptrace-abi.h>
#define CREATE_TRACE_POINTS
#include <trace/events/syscalls.h>
void user_enable_single_step(struct task_struct *child)
{
set_tsk_thread_flag(child, TIF_SINGLESTEP);
#ifdef SUBARCH_SET_SINGLESTEPPING
SUBARCH_SET_SINGLESTEPPING(child, 1);
#endif
}
void user_disable_single_step(struct task_struct *child)
{
clear_tsk_thread_flag(child, TIF_SINGLESTEP);
#ifdef SUBARCH_SET_SINGLESTEPPING
SUBARCH_SET_SINGLESTEPPING(child, 0);
#endif
}
/*
* Called by kernel/ptrace.c when detaching..
*/
void ptrace_disable(struct task_struct *child)
{
user_disable_single_step(child);
}
long arch_ptrace(struct task_struct *child, long request,
unsigned long addr, unsigned long data)
{
int i, ret;
unsigned long __user *p = (void __user *)data;
void __user *vp = p;
switch (request) {
/* read the word at location addr in the USER area. */
case PTRACE_PEEKUSR:
ret = peek_user(child, addr, data);
break;
/* write the word at location addr in the USER area */
case PTRACE_POKEUSR:
ret = poke_user(child, addr, data);
break;
case PTRACE_SYSEMU:
case PTRACE_SYSEMU_SINGLESTEP:
ret = -EIO;
break;
#ifdef PTRACE_GETREGS
case PTRACE_GETREGS: { /* Get all gp regs from the child. */
if (!access_ok(p, MAX_REG_OFFSET)) {
ret = -EIO;
break;
}
for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) {
__put_user(getreg(child, i), p);
p++;
}
ret = 0;
break;
}
#endif
#ifdef PTRACE_SETREGS
case PTRACE_SETREGS: { /* Set all gp regs in the child. */
unsigned long tmp = 0;
if (!access_ok(p, MAX_REG_OFFSET)) {
ret = -EIO;
break;
}
for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) {
__get_user(tmp, p);
putreg(child, i, tmp);
p++;
}
ret = 0;
break;
}
#endif
case PTRACE_GET_THREAD_AREA:
ret = ptrace_get_thread_area(child, addr, vp);
break;
case PTRACE_SET_THREAD_AREA:
ret = ptrace_set_thread_area(child, addr, vp);
break;
default:
ret = ptrace_request(child, request, addr, data);
if (ret == -EIO)
ret = subarch_ptrace(child, request, addr, data);
break;
}
return ret;
}
static void send_sigtrap(struct uml_pt_regs *regs, int error_code)
{
/* Send us the fake SIGTRAP */
force_sig_fault(SIGTRAP, TRAP_BRKPT,
/* User-mode eip? */
UPT_IS_USER(regs) ? (void __user *) UPT_IP(regs) : NULL);
}
/*
* XXX Check TIF_SINGLESTEP for singlestepping check and
* PT_PTRACED vs TIF_SYSCALL_TRACE for syscall tracing check
*/
int syscall_trace_enter(struct pt_regs *regs)
{
audit_syscall_entry(UPT_SYSCALL_NR(&regs->regs),
UPT_SYSCALL_ARG1(&regs->regs),
UPT_SYSCALL_ARG2(&regs->regs),
UPT_SYSCALL_ARG3(&regs->regs),
UPT_SYSCALL_ARG4(&regs->regs));
if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
trace_sys_enter(regs, UPT_SYSCALL_NR(&regs->regs));
if (!test_thread_flag(TIF_SYSCALL_TRACE))
return 0;
return ptrace_report_syscall_entry(regs);
}
void syscall_trace_leave(struct pt_regs *regs)
{
int ptraced = current->ptrace;
audit_syscall_exit(regs);
/* Fake a debug trap */
if (test_thread_flag(TIF_SINGLESTEP))
send_sigtrap(&regs->regs, 0);
if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
trace_sys_exit(regs, PT_REGS_SYSCALL_RET(regs));
if (!test_thread_flag(TIF_SYSCALL_TRACE))
return;
ptrace_report_syscall_exit(regs, 0);
/* force do_signal() --> is_syscall() */
if (ptraced & PT_PTRACED)
set_thread_flag(TIF_SIGPENDING);
}
+76
View File
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/sched/signal.h>
#include <linux/sched/task.h>
#include <linux/sched/mm.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/oom.h>
#include <linux/reboot.h>
#include <kern_util.h>
#include <os.h>
#include <skas.h>
void (*pm_power_off)(void);
EXPORT_SYMBOL(pm_power_off);
static void kill_off_processes(void)
{
struct task_struct *p;
int pid;
read_lock(&tasklist_lock);
for_each_process(p) {
struct task_struct *t;
t = find_lock_task_mm(p);
if (!t)
continue;
pid = t->mm->context.id.pid;
task_unlock(t);
os_kill_ptraced_process(pid, 1);
}
read_unlock(&tasklist_lock);
}
void uml_cleanup(void)
{
kmalloc_ok = 0;
do_uml_exitcalls();
kill_off_processes();
}
void machine_restart(char * __unused)
{
uml_cleanup();
reboot_skas();
}
void machine_power_off(void)
{
uml_cleanup();
halt_skas();
}
void machine_halt(void)
{
machine_power_off();
}
static int sys_power_off_handler(struct sys_off_data *data)
{
machine_power_off();
return 0;
}
static int register_power_off(void)
{
register_sys_off_handler(SYS_OFF_MODE_POWER_OFF,
SYS_OFF_PRIO_DEFAULT,
sys_power_off_handler, NULL);
return 0;
}
__initcall(register_power_off);
+22
View File
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
*/
#include <linux/interrupt.h>
#include <irq_kern.h>
#include <os.h>
#include <sigio.h>
/* These are called from os-Linux/sigio.c to protect its pollfds arrays. */
static DEFINE_MUTEX(sigio_mutex);
void sigio_lock(void)
{
mutex_lock(&sigio_mutex);
}
void sigio_unlock(void)
{
mutex_unlock(&sigio_mutex);
}
+129
View File
@@ -0,0 +1,129 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/module.h>
#include <linux/ptrace.h>
#include <linux/sched.h>
#include <linux/ftrace.h>
#include <asm/siginfo.h>
#include <asm/signal.h>
#include <asm/unistd.h>
#include <frame_kern.h>
#include <kern_util.h>
#include <os.h>
EXPORT_SYMBOL(block_signals);
EXPORT_SYMBOL(unblock_signals);
void block_signals_trace(void)
{
block_signals();
if (current_thread_info())
trace_hardirqs_off();
}
void unblock_signals_trace(void)
{
if (current_thread_info())
trace_hardirqs_on();
unblock_signals();
}
void um_trace_signals_on(void)
{
if (current_thread_info())
trace_hardirqs_on();
}
void um_trace_signals_off(void)
{
if (current_thread_info())
trace_hardirqs_off();
}
/*
* OK, we're invoking a handler
*/
static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)
{
sigset_t *oldset = sigmask_to_save();
int singlestep = 0;
unsigned long sp;
int err;
if (test_thread_flag(TIF_SINGLESTEP) && (current->ptrace & PT_PTRACED))
singlestep = 1;
/* Did we come from a system call? */
if (PT_REGS_SYSCALL_NR(regs) >= 0) {
/* If so, check system call restarting.. */
switch (PT_REGS_SYSCALL_RET(regs)) {
case -ERESTART_RESTARTBLOCK:
case -ERESTARTNOHAND:
PT_REGS_SYSCALL_RET(regs) = -EINTR;
break;
case -ERESTARTSYS:
if (!(ksig->ka.sa.sa_flags & SA_RESTART)) {
PT_REGS_SYSCALL_RET(regs) = -EINTR;
break;
}
fallthrough;
case -ERESTARTNOINTR:
PT_REGS_RESTART_SYSCALL(regs);
PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs);
break;
}
}
sp = PT_REGS_SP(regs);
if ((ksig->ka.sa.sa_flags & SA_ONSTACK) && (sas_ss_flags(sp) == 0))
sp = current->sas_ss_sp + current->sas_ss_size;
#ifdef CONFIG_ARCH_HAS_SC_SIGNALS
if (!(ksig->ka.sa.sa_flags & SA_SIGINFO))
err = setup_signal_stack_sc(sp, ksig, regs, oldset);
else
#endif
err = setup_signal_stack_si(sp, ksig, regs, oldset);
signal_setup_done(err, ksig, singlestep);
}
void do_signal(struct pt_regs *regs)
{
struct ksignal ksig;
int handled_sig = 0;
while (get_signal(&ksig)) {
handled_sig = 1;
/* Whee! Actually deliver the signal. */
handle_signal(&ksig, regs);
}
/* Did we come from a system call? */
if (!handled_sig && (PT_REGS_SYSCALL_NR(regs) >= 0)) {
/* Restart the system call - no handlers present */
switch (PT_REGS_SYSCALL_RET(regs)) {
case -ERESTARTNOHAND:
case -ERESTARTSYS:
case -ERESTARTNOINTR:
PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs);
PT_REGS_RESTART_SYSCALL(regs);
break;
case -ERESTART_RESTARTBLOCK:
PT_REGS_ORIG_SYSCALL(regs) = __NR_restart_syscall;
PT_REGS_RESTART_SYSCALL(regs);
break;
}
}
/*
* if there's no signal to deliver, we just put the saved sigmask
* back
*/
if (!handled_sig)
restore_saved_sigmask();
}
@@ -0,0 +1,2 @@
stub_exe
stub_exe.dbg
+50
View File
@@ -0,0 +1,50 @@
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
#
obj-y := stub.o mmu.o process.o syscall.o uaccess.o \
stub_exe_embed.o
# Stub executable
stub_exe_objs-y := stub_exe.o
stub_exe_objs := $(foreach F,$(stub_exe_objs-y),$(obj)/$F)
# Object file containing the ELF executable
$(obj)/stub_exe_embed.o: $(src)/stub_exe_embed.S $(obj)/stub_exe
$(obj)/stub_exe.dbg: $(stub_exe_objs) FORCE
$(call if_changed,stub_exe)
$(obj)/stub_exe: OBJCOPYFLAGS := -S
$(obj)/stub_exe: $(obj)/stub_exe.dbg FORCE
$(call if_changed,objcopy)
quiet_cmd_stub_exe = STUB_EXE $@
cmd_stub_exe = $(CC) -nostdlib -o $@ \
$(filter-out $(UM_GPROF_OPT) $(UM_GCOV_OPT),$(KBUILD_CFLAGS)) $(STUB_EXE_LDFLAGS) \
$(filter %.o,$^)
STUB_EXE_LDFLAGS = -Wl,-n -static
targets += stub_exe.dbg stub_exe $(stub_exe_objs-y)
# end
# stub.o is in the stub, so it can't be built with profiling
# GCC hardened also auto-enables -fpic, but we need %ebx so it can't work ->
# disable it
CFLAGS_stub.o := $(CFLAGS_NO_HARDENING)
CFLAGS_stub_exe.o := $(CFLAGS_NO_HARDENING)
# Clang will call memset() from __builtin_alloca() when stack variable
# initialization is enabled, which is used in stub_exe.c.
CFLAGS_stub_exe.o += $(call cc-option, -ftrivial-auto-var-init=uninitialized)
UNPROFILE_OBJS := stub.o stub_exe.o
KCOV_INSTRUMENT := n
include $(srctree)/arch/um/scripts/Makefile.rules
+165
View File
@@ -0,0 +1,165 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2015 Thomas Meyer (thomas@m3y3r.de)
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/mm.h>
#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <shared/irq_kern.h>
#include <asm/pgalloc.h>
#include <asm/sections.h>
#include <asm/mmu_context.h>
#include <as-layout.h>
#include <os.h>
#include <skas.h>
#include <stub-data.h>
/* Ensure the stub_data struct covers the allocated area */
static_assert(sizeof(struct stub_data) == STUB_DATA_PAGES * UM_KERN_PAGE_SIZE);
static spinlock_t mm_list_lock;
static struct list_head mm_list;
struct mutex *__get_turnstile(struct mm_id *mm_id)
{
struct mm_context *ctx = container_of(mm_id, struct mm_context, id);
return &ctx->turnstile;
}
void enter_turnstile(struct mm_id *mm_id)
{
mutex_lock(__get_turnstile(mm_id));
}
void exit_turnstile(struct mm_id *mm_id)
{
mutex_unlock(__get_turnstile(mm_id));
}
int init_new_context(struct task_struct *task, struct mm_struct *mm)
{
struct mm_id *new_id = &mm->context.id;
unsigned long stack = 0;
int ret = -ENOMEM;
mutex_init(&mm->context.turnstile);
spin_lock_init(&mm->context.sync_tlb_lock);
stack = __get_free_pages(GFP_KERNEL | __GFP_ZERO, ilog2(STUB_DATA_PAGES));
if (stack == 0)
goto out;
new_id->stack = stack;
new_id->syscall_data_len = 0;
new_id->syscall_fd_num = 0;
scoped_guard(spinlock_irqsave, &mm_list_lock) {
/* Insert into list, used for lookups when the child dies */
list_add(&mm->context.list, &mm_list);
}
ret = start_userspace(new_id);
if (ret < 0)
goto out_free;
/* Ensure the new MM is clean and nothing unwanted is mapped */
unmap(new_id, 0, STUB_START);
return 0;
out_free:
free_pages(new_id->stack, ilog2(STUB_DATA_PAGES));
out:
return ret;
}
void destroy_context(struct mm_struct *mm)
{
struct mm_context *mmu = &mm->context;
/*
* If init_new_context wasn't called, this will be
* zero, resulting in a kill(0), which will result in the
* whole UML suddenly dying. Also, cover negative and
* 1 cases, since they shouldn't happen either.
*
* Negative cases happen if the child died unexpectedly.
*/
if (mmu->id.pid >= 0 && mmu->id.pid < 2) {
printk(KERN_ERR "corrupt mm_context - pid = %d\n",
mmu->id.pid);
return;
}
scoped_guard(spinlock_irqsave, &mm_list_lock)
list_del(&mm->context.list);
if (mmu->id.pid > 0) {
os_kill_ptraced_process(mmu->id.pid, 1);
mmu->id.pid = -1;
}
if (using_seccomp && mmu->id.sock)
os_close_file(mmu->id.sock);
free_pages(mmu->id.stack, ilog2(STUB_DATA_PAGES));
}
static irqreturn_t mm_sigchld_irq(int irq, void* dev)
{
struct mm_context *mm_context;
pid_t pid;
guard(spinlock)(&mm_list_lock);
while ((pid = os_reap_child()) > 0) {
/*
* A child died, check if we have an MM with the PID. This is
* only relevant in SECCOMP mode (as ptrace will fail anyway).
*
* See wait_stub_done_seccomp for more details.
*/
list_for_each_entry(mm_context, &mm_list, list) {
if (mm_context->id.pid == pid) {
struct stub_data *stub_data;
printk("Unexpectedly lost MM child! Affected tasks will segfault.");
/* Marks the MM as dead */
mm_context->id.pid = -1;
stub_data = (void *)mm_context->id.stack;
stub_data->futex = FUTEX_IN_KERN;
#if IS_ENABLED(CONFIG_SMP)
os_futex_wake(&stub_data->futex);
#endif
/*
* NOTE: Currently executing syscalls by
* affected tasks may finish normally.
*/
break;
}
}
}
return IRQ_HANDLED;
}
static int __init init_child_tracking(void)
{
int err;
spin_lock_init(&mm_list_lock);
INIT_LIST_HEAD(&mm_list);
err = request_irq(SIGCHLD_IRQ, mm_sigchld_irq, 0, "SIGCHLD", NULL);
if (err < 0)
panic("Failed to register SIGCHLD IRQ: %d", err);
return 0;
}
early_initcall(init_child_tracking)
+79
View File
@@ -0,0 +1,79 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/init.h>
#include <linux/sched/mm.h>
#include <linux/sched/task_stack.h>
#include <linux/sched/task.h>
#include <linux/smp-internal.h>
#include <asm/tlbflush.h>
#include <as-layout.h>
#include <kern.h>
#include <os.h>
#include <skas.h>
#include <kern_util.h>
extern void start_kernel(void);
static int __init start_kernel_proc(void *unused)
{
block_signals_trace();
start_kernel();
return 0;
}
char cpu_irqstacks[NR_CPUS][THREAD_SIZE] __aligned(THREAD_SIZE);
int __init start_uml(void)
{
stack_protections((unsigned long) &cpu_irqstacks[0]);
set_sigstack(cpu_irqstacks[0], THREAD_SIZE);
init_new_thread_signals();
init_task.thread.request.thread.proc = start_kernel_proc;
init_task.thread.request.thread.arg = NULL;
return start_idle_thread(task_stack_page(&init_task),
&init_task.thread.switch_buf);
}
unsigned long current_stub_stack(void)
{
if (current->mm == NULL)
return 0;
return current->mm->context.id.stack;
}
struct mm_id *current_mm_id(void)
{
if (current->mm == NULL)
return NULL;
return &current->mm->context.id;
}
void current_mm_sync(void)
{
if (current->mm == NULL)
return;
um_tlb_sync(current->mm);
}
static DEFINE_SPINLOCK(initial_jmpbuf_spinlock);
void initial_jmpbuf_lock(void)
{
spin_lock_irq(&initial_jmpbuf_spinlock);
}
void initial_jmpbuf_unlock(void)
{
spin_unlock_irq(&initial_jmpbuf_spinlock);
}
+181
View File
@@ -0,0 +1,181 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 Benjamin Berg <benjamin@sipsolutions.net>
*/
#include <sysdep/stub.h>
#include <linux/futex.h>
#include <sys/socket.h>
#include <errno.h>
/*
* Known security issues
*
* Userspace can jump to this address to execute *any* syscall that is
* permitted by the stub. As we will return afterwards, it can do
* whatever it likes, including:
* - Tricking the kernel into handing out the memory FD
* - Using this memory FD to read/write all physical memory
* - Running in parallel to the kernel processing a syscall
* (possibly creating data races?)
* - Blocking e.g. SIGALRM to avoid time based scheduling
*
* To avoid this, the permitted location for each syscall needs to be
* checked for in the SECCOMP filter (which is reasonably simple). Also,
* more care will need to go into considerations how the code might be
* tricked by using a prepared stack (or even modifying the stack from
* another thread in case SMP support is added).
*
* As for the SIGALRM, the best counter measure will be to check in the
* kernel that the process is reporting back the SIGALRM in a timely
* fashion.
*/
static __always_inline int syscall_handler(int fd_map[STUB_MAX_FDS])
{
struct stub_data *d = get_stub_data();
int i;
unsigned long res;
int fd;
for (i = 0; i < d->syscall_data_len; i++) {
struct stub_syscall *sc = &d->syscall_data[i];
switch (sc->syscall) {
case STUB_SYSCALL_MMAP:
if (fd_map)
fd = fd_map[sc->mem.fd];
else
fd = sc->mem.fd;
res = stub_syscall6(STUB_MMAP_NR,
sc->mem.addr, sc->mem.length,
sc->mem.prot,
MAP_SHARED | MAP_FIXED,
fd, sc->mem.offset);
if (res != sc->mem.addr) {
d->err = res;
d->syscall_data_len = i;
return -1;
}
break;
case STUB_SYSCALL_MUNMAP:
res = stub_syscall2(__NR_munmap,
sc->mem.addr, sc->mem.length);
if (res) {
d->err = res;
d->syscall_data_len = i;
return -1;
}
break;
default:
d->err = -95; /* EOPNOTSUPP */
d->syscall_data_len = i;
return -1;
}
}
d->err = 0;
d->syscall_data_len = 0;
return 0;
}
void __section(".__syscall_stub")
stub_syscall_handler(void)
{
syscall_handler(NULL);
trap_myself();
}
void __section(".__syscall_stub")
stub_signal_interrupt(int sig, siginfo_t *info, void *p)
{
struct stub_data *d = get_stub_data();
char rcv_data;
union {
char data[CMSG_SPACE(sizeof(int) * STUB_MAX_FDS)];
struct cmsghdr align;
} ctrl = {};
struct iovec iov = {
.iov_base = &rcv_data,
.iov_len = 1,
};
struct msghdr msghdr = {
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = &ctrl,
.msg_controllen = sizeof(ctrl),
};
ucontext_t *uc = p;
struct cmsghdr *fd_msg;
int *fd_map;
int num_fds;
long res;
d->signal = sig;
d->si_offset = (unsigned long)info - (unsigned long)&d->sigstack[0];
d->mctx_offset = (unsigned long)&uc->uc_mcontext - (unsigned long)&d->sigstack[0];
restart_wait:
d->futex = FUTEX_IN_KERN;
do {
res = stub_syscall3(__NR_futex, (unsigned long)&d->futex,
FUTEX_WAKE, 1);
} while (res == -EINTR);
do {
res = stub_syscall4(__NR_futex, (unsigned long)&d->futex,
FUTEX_WAIT, FUTEX_IN_KERN, 0);
} while (res == -EINTR || d->futex == FUTEX_IN_KERN);
if (res < 0 && res != -EAGAIN)
stub_syscall1(__NR_exit_group, 1);
if (d->syscall_data_len) {
/* Read passed FDs (if any) */
do {
res = stub_syscall3(__NR_recvmsg, 0, (unsigned long)&msghdr, 0);
} while (res == -EINTR);
/* We should never have a receive error (other than -EAGAIN) */
if (res < 0 && res != -EAGAIN)
stub_syscall1(__NR_exit_group, 1);
/* Receive the FDs */
num_fds = 0;
fd_msg = msghdr.msg_control;
fd_map = (void *)CMSG_DATA(fd_msg);
if (res == iov.iov_len && msghdr.msg_controllen > sizeof(struct cmsghdr))
num_fds = (fd_msg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
/* Try running queued syscalls. */
res = syscall_handler(fd_map);
while (num_fds)
stub_syscall2(__NR_close, fd_map[--num_fds], 0);
} else {
res = 0;
}
if (res < 0 || d->restart_wait) {
/* Report SIGSYS if we restart. */
d->signal = SIGSYS;
d->restart_wait = 0;
goto restart_wait;
}
/* Restore arch dependent state that is not part of the mcontext */
stub_seccomp_restore_state(&d->arch_data);
/* Return so that the host modified mcontext is restored. */
}
void __section(".__syscall_stub")
stub_signal_restorer(void)
{
/* We must not have anything on the stack when doing rt_sigreturn */
stub_syscall0(__NR_rt_sigreturn);
}
+230
View File
@@ -0,0 +1,230 @@
#include <sys/ptrace.h>
#include <sys/prctl.h>
#include <sys/fcntl.h>
#include <asm/unistd.h>
#include <sysdep/stub.h>
#include <stub-data.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <generated/asm-offsets.h>
void _start(void);
noinline static void real_init(void)
{
struct stub_init_data init_data;
unsigned long res;
struct {
void *ss_sp;
int ss_flags;
size_t ss_size;
} stack = {
.ss_size = STUB_DATA_PAGES * UM_KERN_PAGE_SIZE,
};
struct {
void *sa_handler_;
unsigned long sa_flags;
void *sa_restorer;
unsigned long long sa_mask;
} sa = {
/* Need to set SA_RESTORER (but the handler never returns) */
.sa_flags = SA_ONSTACK | SA_NODEFER | SA_SIGINFO | 0x04000000,
};
/* set a nice name */
stub_syscall2(__NR_prctl, PR_SET_NAME, (unsigned long)"uml-userspace");
/* Make sure this process dies if the kernel dies */
stub_syscall2(__NR_prctl, PR_SET_PDEATHSIG, SIGKILL);
/* Needed in SECCOMP mode (and safe to do anyway) */
stub_syscall5(__NR_prctl, PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
/* read information from STDIN and close it */
res = stub_syscall3(__NR_read, 0,
(unsigned long)&init_data, sizeof(init_data));
if (res != sizeof(init_data))
stub_syscall1(__NR_exit, 10);
/* In SECCOMP mode, FD 0 is a socket and is later used for FD passing */
if (!init_data.seccomp)
stub_syscall1(__NR_close, 0);
else
stub_syscall3(__NR_fcntl, 0, F_SETFL, O_NONBLOCK);
/* map stub code + data */
res = stub_syscall6(STUB_MMAP_NR,
init_data.stub_start, UM_KERN_PAGE_SIZE,
PROT_READ | PROT_EXEC, MAP_FIXED | MAP_SHARED,
init_data.stub_code_fd, init_data.stub_code_offset);
if (res != init_data.stub_start)
stub_syscall1(__NR_exit, 11);
res = stub_syscall6(STUB_MMAP_NR,
init_data.stub_start + UM_KERN_PAGE_SIZE,
STUB_DATA_PAGES * UM_KERN_PAGE_SIZE,
PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED,
init_data.stub_data_fd, init_data.stub_data_offset);
if (res != init_data.stub_start + UM_KERN_PAGE_SIZE)
stub_syscall1(__NR_exit, 12);
/* In SECCOMP mode, we only need the signalling FD from now on */
if (init_data.seccomp) {
res = stub_syscall3(__NR_close_range, 1, ~0U, 0);
if (res != 0)
stub_syscall1(__NR_exit, 13);
}
/* setup signal stack inside stub data */
stack.ss_sp = (void *)init_data.stub_start + UM_KERN_PAGE_SIZE;
stub_syscall2(__NR_sigaltstack, (unsigned long)&stack, 0);
/* register signal handlers */
sa.sa_handler_ = (void *) init_data.signal_handler;
sa.sa_restorer = (void *) init_data.signal_restorer;
if (!init_data.seccomp) {
/* In ptrace mode, the SIGSEGV handler never returns */
sa.sa_mask = 0;
res = stub_syscall4(__NR_rt_sigaction, SIGSEGV,
(unsigned long)&sa, 0, sizeof(sa.sa_mask));
if (res != 0)
stub_syscall1(__NR_exit, 14);
} else {
/* SECCOMP mode uses rt_sigreturn, need to mask all signals */
sa.sa_mask = ~0ULL;
res = stub_syscall4(__NR_rt_sigaction, SIGSEGV,
(unsigned long)&sa, 0, sizeof(sa.sa_mask));
if (res != 0)
stub_syscall1(__NR_exit, 15);
res = stub_syscall4(__NR_rt_sigaction, SIGSYS,
(unsigned long)&sa, 0, sizeof(sa.sa_mask));
if (res != 0)
stub_syscall1(__NR_exit, 16);
res = stub_syscall4(__NR_rt_sigaction, SIGALRM,
(unsigned long)&sa, 0, sizeof(sa.sa_mask));
if (res != 0)
stub_syscall1(__NR_exit, 17);
res = stub_syscall4(__NR_rt_sigaction, SIGTRAP,
(unsigned long)&sa, 0, sizeof(sa.sa_mask));
if (res != 0)
stub_syscall1(__NR_exit, 18);
res = stub_syscall4(__NR_rt_sigaction, SIGILL,
(unsigned long)&sa, 0, sizeof(sa.sa_mask));
if (res != 0)
stub_syscall1(__NR_exit, 19);
res = stub_syscall4(__NR_rt_sigaction, SIGFPE,
(unsigned long)&sa, 0, sizeof(sa.sa_mask));
if (res != 0)
stub_syscall1(__NR_exit, 20);
}
/*
* If in seccomp mode, install the SECCOMP filter and trigger a syscall.
* Otherwise set PTRACE_TRACEME and do a SIGSTOP.
*/
if (init_data.seccomp) {
struct sock_filter filter[] = {
#if __BITS_PER_LONG > 32
/* [0] Load upper 32bit of instruction pointer from seccomp_data */
BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
(offsetof(struct seccomp_data, instruction_pointer) + 4)),
/* [1] Jump forward 3 instructions if the upper address is not identical */
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (init_data.stub_start) >> 32, 0, 3),
#endif
/* [2] Load lower 32bit of instruction pointer from seccomp_data */
BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
(offsetof(struct seccomp_data, instruction_pointer))),
/* [3] Mask out lower bits */
BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xfffff000),
/* [4] Jump to [6] if the lower bits are not on the expected page */
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (init_data.stub_start) & 0xfffff000, 1, 0),
/* [5] Trap call, allow */
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRAP),
/* [6,7] Check architecture */
BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
offsetof(struct seccomp_data, arch)),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,
UM_SECCOMP_ARCH_NATIVE, 1, 0),
/* [8] Kill (for architecture check) */
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
/* [9] Load syscall number */
BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
offsetof(struct seccomp_data, nr)),
/* [10-16] Check against permitted syscalls */
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_futex,
7, 0),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,__NR_recvmsg,
6, 0),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,__NR_close,
5, 0),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, STUB_MMAP_NR,
4, 0),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_munmap,
3, 0),
#ifdef __i386__
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_set_thread_area,
2, 0),
#else
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_arch_prctl,
2, 0),
#endif
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_rt_sigreturn,
1, 0),
/* [17] Not one of the permitted syscalls */
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
/* [18] Permitted call for the stub */
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
};
struct sock_fprog prog = {
.len = sizeof(filter) / sizeof(filter[0]),
.filter = filter,
};
if (stub_syscall3(__NR_seccomp, SECCOMP_SET_MODE_FILTER,
SECCOMP_FILTER_FLAG_TSYNC,
(unsigned long)&prog) != 0)
stub_syscall1(__NR_exit, 21);
/* Fall through, the exit syscall will cause SIGSYS */
} else {
stub_syscall4(__NR_ptrace, PTRACE_TRACEME, 0, 0, 0);
stub_syscall2(__NR_kill, stub_syscall0(__NR_getpid), SIGSTOP);
}
stub_syscall1(__NR_exit, 30);
__builtin_unreachable();
}
__attribute__((naked)) void _start(void)
{
/*
* Since the stack after exec() starts at the top-most address,
* but that's exactly where we also want to map the stub data
* and code, this must:
* - push the stack by 1 code and STUB_DATA_PAGES data pages
* - call real_init()
* This way, real_init() can use the stack normally, while the
* original stack further down (higher address) will become
* inaccessible after the mmap() calls above.
*/
stub_start(real_init);
}
@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/init.h>
#include <linux/linkage.h>
__INITDATA
SYM_DATA_START(stub_exe_start)
.incbin "arch/um/kernel/skas/stub_exe"
SYM_DATA_END_LABEL(stub_exe_start, SYM_L_GLOBAL, stub_exe_end)
__FINIT
+74
View File
@@ -0,0 +1,74 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/kernel.h>
#include <linux/ptrace.h>
#include <linux/seccomp.h>
#include <kern_util.h>
#include <sysdep/ptrace.h>
#include <sysdep/ptrace_user.h>
#include <linux/time-internal.h>
#include <asm/syscall.h>
#include <asm/unistd.h>
#include <asm/delay.h>
void handle_syscall(struct uml_pt_regs *r)
{
struct pt_regs *regs = container_of(r, struct pt_regs, regs);
int syscall;
/* Initialize the syscall number and default return value. */
UPT_SYSCALL_NR(r) = PT_SYSCALL_NR(r->gp);
PT_REGS_SET_SYSCALL_RETURN(regs, -ENOSYS);
if (syscall_trace_enter(regs))
goto out;
/* Do the seccomp check after ptrace; failures should be fast. */
if (secure_computing() == -1)
goto out;
syscall = UPT_SYSCALL_NR(r);
/*
* If no time passes, then sched_yield may not actually yield, causing
* broken spinlock implementations in userspace (ASAN) to hang for long
* periods of time.
*/
if ((time_travel_mode == TT_MODE_INFCPU ||
time_travel_mode == TT_MODE_EXTERNAL) &&
syscall == __NR_sched_yield)
tt_extra_sched_jiffies += 1;
if (syscall >= 0 && syscall < __NR_syscalls) {
unsigned long ret;
ret = (*sys_call_table[syscall])(UPT_SYSCALL_ARG1(&regs->regs),
UPT_SYSCALL_ARG2(&regs->regs),
UPT_SYSCALL_ARG3(&regs->regs),
UPT_SYSCALL_ARG4(&regs->regs),
UPT_SYSCALL_ARG5(&regs->regs),
UPT_SYSCALL_ARG6(&regs->regs));
PT_REGS_SET_SYSCALL_RETURN(regs, ret);
/*
* An error value here can be some form of -ERESTARTSYS
* and then we'd just loop. Make any error syscalls take
* some time, so that it won't just loop if something is
* not ready, and hopefully other things will make some
* progress.
*/
if (IS_ERR_VALUE(ret) &&
(time_travel_mode == TT_MODE_INFCPU ||
time_travel_mode == TT_MODE_EXTERNAL)) {
um_udelay(1);
schedule();
}
}
out:
syscall_trace_leave(regs);
}
+368
View File
@@ -0,0 +1,368 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/err.h>
#include <linux/highmem.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <asm/current.h>
#include <asm/page.h>
#include <kern_util.h>
#include <asm/futex.h>
#include <os.h>
pte_t *virt_to_pte(struct mm_struct *mm, unsigned long addr)
{
pgd_t *pgd;
p4d_t *p4d;
pud_t *pud;
pmd_t *pmd;
if (mm == NULL)
return NULL;
pgd = pgd_offset(mm, addr);
if (!pgd_present(*pgd))
return NULL;
p4d = p4d_offset(pgd, addr);
if (!p4d_present(*p4d))
return NULL;
pud = pud_offset(p4d, addr);
if (!pud_present(*pud))
return NULL;
pmd = pmd_offset(pud, addr);
if (!pmd_present(*pmd))
return NULL;
return pte_offset_kernel(pmd, addr);
}
static pte_t *maybe_map(unsigned long virt, int is_write)
{
pte_t *pte = virt_to_pte(current->mm, virt);
int err, dummy_code;
if ((pte == NULL) || !pte_present(*pte) ||
(is_write && !pte_write(*pte))) {
err = handle_page_fault(virt, 0, is_write, 1, &dummy_code);
if (err)
return NULL;
pte = virt_to_pte(current->mm, virt);
}
if (!pte_present(*pte))
pte = NULL;
return pte;
}
static int do_op_one_page(unsigned long addr, int len, int is_write,
int (*op)(unsigned long addr, int len, void *arg), void *arg)
{
struct page *page;
pte_t *pte;
int n;
pte = maybe_map(addr, is_write);
if (pte == NULL)
return -1;
page = pte_page(*pte);
#ifdef CONFIG_64BIT
pagefault_disable();
addr = (unsigned long) page_address(page) +
(addr & ~PAGE_MASK);
#else
addr = (unsigned long) kmap_atomic(page) +
(addr & ~PAGE_MASK);
#endif
n = (*op)(addr, len, arg);
#ifdef CONFIG_64BIT
pagefault_enable();
#else
kunmap_atomic((void *)addr);
#endif
return n;
}
static long buffer_op(unsigned long addr, int len, int is_write,
int (*op)(unsigned long, int, void *), void *arg)
{
long size, remain, n;
size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len);
remain = len;
n = do_op_one_page(addr, size, is_write, op, arg);
if (n != 0) {
remain = (n < 0 ? remain : 0);
goto out;
}
addr += size;
remain -= size;
if (remain == 0)
goto out;
while (addr < ((addr + remain) & PAGE_MASK)) {
n = do_op_one_page(addr, PAGE_SIZE, is_write, op, arg);
if (n != 0) {
remain = (n < 0 ? remain : 0);
goto out;
}
addr += PAGE_SIZE;
remain -= PAGE_SIZE;
}
if (remain == 0)
goto out;
n = do_op_one_page(addr, remain, is_write, op, arg);
if (n != 0) {
remain = (n < 0 ? remain : 0);
goto out;
}
return 0;
out:
return remain;
}
static int copy_chunk_from_user(unsigned long from, int len, void *arg)
{
unsigned long *to_ptr = arg, to = *to_ptr;
memcpy((void *) to, (void *) from, len);
*to_ptr += len;
return 0;
}
unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n)
{
return buffer_op((unsigned long) from, n, 0, copy_chunk_from_user, &to);
}
EXPORT_SYMBOL(raw_copy_from_user);
static int copy_chunk_to_user(unsigned long to, int len, void *arg)
{
unsigned long *from_ptr = arg, from = *from_ptr;
memcpy((void *) to, (void *) from, len);
*from_ptr += len;
return 0;
}
unsigned long raw_copy_to_user(void __user *to, const void *from, unsigned long n)
{
return buffer_op((unsigned long) to, n, 1, copy_chunk_to_user, &from);
}
EXPORT_SYMBOL(raw_copy_to_user);
static int strncpy_chunk_from_user(unsigned long from, int len, void *arg)
{
char **to_ptr = arg, *to = *to_ptr;
int n;
n = strnlen((void *) from, len);
memcpy_and_pad(to, len, (void *) from, n, 0);
*to_ptr += n;
if (n < len)
return 1;
return 0;
}
long strncpy_from_user(char *dst, const char __user *src, long count)
{
long n;
char *ptr = dst;
if (!access_ok(src, 1))
return -EFAULT;
n = buffer_op((unsigned long) src, count, 0, strncpy_chunk_from_user,
&ptr);
if (n != 0)
return -EFAULT;
return strnlen(dst, count);
}
EXPORT_SYMBOL(strncpy_from_user);
static int clear_chunk(unsigned long addr, int len, void *unused)
{
memset((void *) addr, 0, len);
return 0;
}
unsigned long __clear_user(void __user *mem, unsigned long len)
{
return buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL);
}
EXPORT_SYMBOL(__clear_user);
static int strnlen_chunk(unsigned long str, int len, void *arg)
{
int *len_ptr = arg, n;
n = strnlen((void *) str, len);
*len_ptr += n;
if (n < len)
return 1;
return 0;
}
long strnlen_user(const char __user *str, long len)
{
int count = 0, n;
if (!access_ok(str, 1))
return -EFAULT;
n = buffer_op((unsigned long) str, len, 0, strnlen_chunk, &count);
if (n == 0)
return count + 1;
return 0;
}
EXPORT_SYMBOL(strnlen_user);
/**
* arch_futex_atomic_op_inuser() - Atomic arithmetic operation with constant
* argument and comparison of the previous
* futex value with another constant.
*
* @op: operation to execute
* @oparg: argument to operation
* @oval: old value at uaddr
* @uaddr: pointer to user space address
*
* Return:
* 0 - On success
* -EFAULT - User access resulted in a page fault
* -EAGAIN - Atomic operation was unable to complete due to contention
* -ENOSYS - Operation not supported
*/
int arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
{
int oldval, ret;
struct page *page;
unsigned long addr = (unsigned long) uaddr;
pte_t *pte;
ret = -EFAULT;
if (!access_ok(uaddr, sizeof(*uaddr)))
return -EFAULT;
preempt_disable();
pte = maybe_map(addr, 1);
if (pte == NULL)
goto out_inuser;
page = pte_page(*pte);
#ifdef CONFIG_64BIT
pagefault_disable();
addr = (unsigned long) page_address(page) +
(((unsigned long) addr) & ~PAGE_MASK);
#else
addr = (unsigned long) kmap_atomic(page) +
((unsigned long) addr & ~PAGE_MASK);
#endif
uaddr = (u32 *) addr;
oldval = *uaddr;
ret = 0;
switch (op) {
case FUTEX_OP_SET:
*uaddr = oparg;
break;
case FUTEX_OP_ADD:
*uaddr += oparg;
break;
case FUTEX_OP_OR:
*uaddr |= oparg;
break;
case FUTEX_OP_ANDN:
*uaddr &= ~oparg;
break;
case FUTEX_OP_XOR:
*uaddr ^= oparg;
break;
default:
ret = -ENOSYS;
}
#ifdef CONFIG_64BIT
pagefault_enable();
#else
kunmap_atomic((void *)addr);
#endif
out_inuser:
preempt_enable();
if (ret == 0)
*oval = oldval;
return ret;
}
EXPORT_SYMBOL(arch_futex_atomic_op_inuser);
/**
* futex_atomic_cmpxchg_inatomic() - Compare and exchange the content of the
* uaddr with newval if the current value is
* oldval.
* @uval: pointer to store content of @uaddr
* @uaddr: pointer to user space address
* @oldval: old value
* @newval: new value to store to @uaddr
*
* Return:
* 0 - On success
* -EFAULT - User access resulted in a page fault
* -EAGAIN - Atomic operation was unable to complete due to contention
*/
int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
u32 oldval, u32 newval)
{
struct page *page;
pte_t *pte;
int ret = -EFAULT;
if (!access_ok(uaddr, sizeof(*uaddr)))
return -EFAULT;
preempt_disable();
pte = maybe_map((unsigned long) uaddr, 1);
if (pte == NULL)
goto out_inatomic;
page = pte_page(*pte);
#ifdef CONFIG_64BIT
pagefault_disable();
uaddr = page_address(page) + (((unsigned long) uaddr) & ~PAGE_MASK);
#else
uaddr = kmap_atomic(page) + ((unsigned long) uaddr & ~PAGE_MASK);
#endif
*uval = *uaddr;
ret = cmpxchg(uaddr, oldval, newval);
#ifdef CONFIG_64BIT
pagefault_enable();
#else
kunmap_atomic(uaddr);
#endif
ret = 0;
out_inatomic:
preempt_enable();
return ret;
}
EXPORT_SYMBOL(futex_atomic_cmpxchg_inatomic);
+242
View File
@@ -0,0 +1,242 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2025 Ant Group
* Author: Tiwei Bie <tiwei.btw@antgroup.com>
*
* Based on the previous implementation in TT mode
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/sched.h>
#include <linux/sched/task.h>
#include <linux/sched/task_stack.h>
#include <linux/module.h>
#include <linux/processor.h>
#include <linux/threads.h>
#include <linux/cpu.h>
#include <linux/hardirq.h>
#include <linux/smp.h>
#include <linux/smp-internal.h>
#include <init.h>
#include <kern.h>
#include <os.h>
#include <smp.h>
enum {
UML_IPI_RES = 0,
UML_IPI_CALL_SINGLE,
UML_IPI_CALL,
UML_IPI_STOP,
};
void arch_smp_send_reschedule(int cpu)
{
os_send_ipi(cpu, UML_IPI_RES);
}
void arch_send_call_function_single_ipi(int cpu)
{
os_send_ipi(cpu, UML_IPI_CALL_SINGLE);
}
void arch_send_call_function_ipi_mask(const struct cpumask *mask)
{
int cpu;
for_each_cpu(cpu, mask)
os_send_ipi(cpu, UML_IPI_CALL);
}
void smp_send_stop(void)
{
int cpu, me = smp_processor_id();
for_each_online_cpu(cpu) {
if (cpu == me)
continue;
os_send_ipi(cpu, UML_IPI_STOP);
}
}
static void ipi_handler(int vector, struct uml_pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs((struct pt_regs *)regs);
int cpu = raw_smp_processor_id();
irq_enter();
if (current->mm)
os_alarm_process(current->mm->context.id.pid);
switch (vector) {
case UML_IPI_RES:
inc_irq_stat(irq_resched_count);
scheduler_ipi();
break;
case UML_IPI_CALL_SINGLE:
inc_irq_stat(irq_call_count);
generic_smp_call_function_single_interrupt();
break;
case UML_IPI_CALL:
inc_irq_stat(irq_call_count);
generic_smp_call_function_interrupt();
break;
case UML_IPI_STOP:
set_cpu_online(cpu, false);
while (1)
pause();
break;
default:
pr_err("CPU#%d received unknown IPI (vector=%d)!\n", cpu, vector);
break;
}
irq_exit();
set_irq_regs(old_regs);
}
void uml_ipi_handler(int vector)
{
struct uml_pt_regs r = { .is_user = 0 };
preempt_disable();
ipi_handler(vector, &r);
preempt_enable();
}
/* AP states used only during CPU startup */
enum {
UML_CPU_PAUSED = 0,
UML_CPU_RUNNING,
};
static int cpu_states[NR_CPUS];
static int start_secondary(void *unused)
{
int err, cpu = raw_smp_processor_id();
notify_cpu_starting(cpu);
set_cpu_online(cpu, true);
err = um_setup_timer();
if (err)
panic("CPU#%d failed to setup timer, err = %d", cpu, err);
local_irq_enable();
cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
return 0;
}
void uml_start_secondary(void *opaque)
{
int cpu = raw_smp_processor_id();
struct mm_struct *mm = &init_mm;
struct task_struct *idle;
stack_protections((unsigned long) &cpu_irqstacks[cpu]);
set_sigstack(&cpu_irqstacks[cpu], THREAD_SIZE);
set_cpu_present(cpu, true);
os_futex_wait(&cpu_states[cpu], UML_CPU_PAUSED);
smp_rmb(); /* paired with smp_wmb() in __cpu_up() */
idle = cpu_tasks[cpu];
idle->thread_info.cpu = cpu;
mmgrab(mm);
idle->active_mm = mm;
idle->thread.request.thread.proc = start_secondary;
idle->thread.request.thread.arg = NULL;
new_thread(task_stack_page(idle), &idle->thread.switch_buf,
new_thread_handler);
os_start_secondary(opaque, &idle->thread.switch_buf);
}
void __init smp_prepare_cpus(unsigned int max_cpus)
{
int err, cpu, me = smp_processor_id();
unsigned long deadline;
os_init_smp();
for_each_possible_cpu(cpu) {
if (cpu == me)
continue;
pr_debug("Booting processor %d...\n", cpu);
err = os_start_cpu_thread(cpu);
if (err) {
pr_crit("CPU#%d failed to start cpu thread, err = %d",
cpu, err);
continue;
}
deadline = jiffies + msecs_to_jiffies(1000);
spin_until_cond(cpu_present(cpu) ||
time_is_before_jiffies(deadline));
if (!cpu_present(cpu))
pr_crit("CPU#%d failed to boot\n", cpu);
}
}
int __cpu_up(unsigned int cpu, struct task_struct *tidle)
{
cpu_tasks[cpu] = tidle;
smp_wmb(); /* paired with smp_rmb() in uml_start_secondary() */
cpu_states[cpu] = UML_CPU_RUNNING;
os_futex_wake(&cpu_states[cpu]);
spin_until_cond(cpu_online(cpu));
return 0;
}
void __init smp_cpus_done(unsigned int max_cpus)
{
}
/* Set in uml_ncpus_setup */
int uml_ncpus = 1;
void __init prefill_possible_map(void)
{
int cpu;
for (cpu = 0; cpu < uml_ncpus; cpu++)
set_cpu_possible(cpu, true);
for (; cpu < NR_CPUS; cpu++)
set_cpu_possible(cpu, false);
}
static int __init uml_ncpus_setup(char *line, int *add)
{
*add = 0;
if (kstrtoint(line, 10, &uml_ncpus)) {
os_warn("%s: Couldn't parse '%s'\n", __func__, line);
return -1;
}
uml_ncpus = clamp(uml_ncpus, 1, NR_CPUS);
return 0;
}
__uml_setup("ncpus=", uml_ncpus_setup,
"ncpus=<# of desired CPUs>\n"
" This tells UML how many virtual processors to start. The maximum\n"
" number of supported virtual processors can be obtained by querying\n"
" the CONFIG_NR_CPUS option using --showconfig.\n\n"
);
EXPORT_SYMBOL(uml_curr_cpu);
+75
View File
@@ -0,0 +1,75 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Copyright (C) 2013 Richard Weinberger <richard@nod.at>
* Copyright (C) 2014 Google Inc., Author: Daniel Walter <dwalter@google.com>
*/
#include <linux/kallsyms.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/stacktrace.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <asm/stacktrace.h>
void dump_trace(struct task_struct *tsk,
const struct stacktrace_ops *ops,
void *data)
{
int reliable = 0;
unsigned long *sp, bp, addr;
struct pt_regs *segv_regs = tsk->thread.segv_regs;
struct stack_frame *frame;
bp = get_frame_pointer(tsk, segv_regs);
sp = get_stack_pointer(tsk, segv_regs);
frame = (struct stack_frame *)bp;
while (((long) sp & (THREAD_SIZE-1)) != 0) {
addr = READ_ONCE_NOCHECK(*sp);
if (__kernel_text_address(addr)) {
reliable = 0;
if ((unsigned long) sp == bp + sizeof(long)) {
frame = frame ? frame->next_frame : NULL;
bp = (unsigned long)frame;
reliable = 1;
}
ops->address(data, addr, reliable);
}
sp++;
}
}
static void save_addr(void *data, unsigned long address, int reliable)
{
struct stack_trace *trace = data;
if (!reliable)
return;
if (trace->nr_entries >= trace->max_entries)
return;
trace->entries[trace->nr_entries++] = address;
}
static const struct stacktrace_ops dump_ops = {
.address = save_addr
};
static void __save_stack_trace(struct task_struct *tsk, struct stack_trace *trace)
{
dump_trace(tsk, &dump_ops, trace);
}
void save_stack_trace(struct stack_trace *trace)
{
__save_stack_trace(current, trace);
}
EXPORT_SYMBOL_GPL(save_stack_trace);
void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
{
__save_stack_trace(tsk, trace);
}
EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
+50
View File
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Copyright (C) 2013 Richard Weinberger <richrd@nod.at>
*/
#include <linux/kallsyms.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/sched/debug.h>
#include <linux/sched/task_stack.h>
#include <asm/stacktrace.h>
#include <os.h>
static void _print_addr(void *data, unsigned long address, int reliable)
{
const char *loglvl = data;
printk("%s [<%08lx>] %s%pS\n", loglvl, address, reliable ? "" : "? ",
(void *)address);
}
static const struct stacktrace_ops stackops = {
.address = _print_addr
};
void show_stack(struct task_struct *task, unsigned long *stack,
const char *loglvl)
{
struct pt_regs *segv_regs = current->thread.segv_regs;
int i;
if (!stack)
stack = get_stack_pointer(task, segv_regs);
printk("%sStack:\n", loglvl);
for (i = 0; i < 3 * STACKSLOTS_PER_LINE; i++) {
if (kstack_end(stack))
break;
if (i && ((i % STACKSLOTS_PER_LINE) == 0))
pr_cont("\n");
pr_cont(" %08lx", READ_ONCE_NOCHECK(*stack));
stack++;
}
printk("%sCall Trace:\n", loglvl);
dump_trace(task ?: current, &stackops, (void *)loglvl);
}
File diff suppressed because it is too large Load Diff
+225
View File
@@ -0,0 +1,225 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/sched/signal.h>
#include <asm/tlbflush.h>
#include <asm/mmu_context.h>
#include <as-layout.h>
#include <mem_user.h>
#include <os.h>
#include <skas.h>
#include <kern_util.h>
struct vm_ops {
struct mm_id *mm_idp;
int (*mmap)(struct mm_id *mm_idp,
unsigned long virt, unsigned long len, int prot,
int phys_fd, unsigned long long offset);
int (*unmap)(struct mm_id *mm_idp,
unsigned long virt, unsigned long len);
};
static int kern_map(struct mm_id *mm_idp,
unsigned long virt, unsigned long len, int prot,
int phys_fd, unsigned long long offset)
{
return os_map_memory((void *)virt, phys_fd, offset, len,
prot & UM_PROT_READ, prot & UM_PROT_WRITE,
prot & UM_PROT_EXEC);
}
static int kern_unmap(struct mm_id *mm_idp,
unsigned long virt, unsigned long len)
{
return os_unmap_memory((void *)virt, len);
}
void report_enomem(void)
{
printk(KERN_ERR "UML ran out of memory on the host side! "
"This can happen due to a memory limitation or "
"vm.max_map_count has been reached.\n");
}
static inline int update_pte_range(pmd_t *pmd, unsigned long addr,
unsigned long end,
struct vm_ops *ops)
{
pte_t *pte;
int ret = 0;
pte = pte_offset_kernel(pmd, addr);
do {
if (!pte_needsync(*pte))
continue;
if (pte_present(*pte)) {
__u64 offset;
unsigned long phys = pte_val(*pte) & PAGE_MASK;
int fd = phys_mapping(phys, &offset);
int r, w, x, prot;
r = pte_read(*pte);
w = pte_write(*pte);
x = pte_exec(*pte);
if (!pte_young(*pte)) {
r = 0;
w = 0;
} else if (!pte_dirty(*pte))
w = 0;
prot = (r ? UM_PROT_READ : 0) |
(w ? UM_PROT_WRITE : 0) |
(x ? UM_PROT_EXEC : 0);
ret = ops->mmap(ops->mm_idp, addr, PAGE_SIZE,
prot, fd, offset);
} else
ret = ops->unmap(ops->mm_idp, addr, PAGE_SIZE);
*pte = pte_mkuptodate(*pte);
} while (pte++, addr += PAGE_SIZE, ((addr < end) && !ret));
return ret;
}
static inline int update_pmd_range(pud_t *pud, unsigned long addr,
unsigned long end,
struct vm_ops *ops)
{
pmd_t *pmd;
unsigned long next;
int ret = 0;
pmd = pmd_offset(pud, addr);
do {
next = pmd_addr_end(addr, end);
if (!pmd_present(*pmd)) {
if (pmd_needsync(*pmd)) {
ret = ops->unmap(ops->mm_idp, addr,
next - addr);
pmd_mkuptodate(*pmd);
}
}
else ret = update_pte_range(pmd, addr, next, ops);
} while (pmd++, addr = next, ((addr < end) && !ret));
return ret;
}
static inline int update_pud_range(p4d_t *p4d, unsigned long addr,
unsigned long end,
struct vm_ops *ops)
{
pud_t *pud;
unsigned long next;
int ret = 0;
pud = pud_offset(p4d, addr);
do {
next = pud_addr_end(addr, end);
if (!pud_present(*pud)) {
if (pud_needsync(*pud)) {
ret = ops->unmap(ops->mm_idp, addr,
next - addr);
pud_mkuptodate(*pud);
}
}
else ret = update_pmd_range(pud, addr, next, ops);
} while (pud++, addr = next, ((addr < end) && !ret));
return ret;
}
static inline int update_p4d_range(pgd_t *pgd, unsigned long addr,
unsigned long end,
struct vm_ops *ops)
{
p4d_t *p4d;
unsigned long next;
int ret = 0;
p4d = p4d_offset(pgd, addr);
do {
next = p4d_addr_end(addr, end);
if (!p4d_present(*p4d)) {
if (p4d_needsync(*p4d)) {
ret = ops->unmap(ops->mm_idp, addr,
next - addr);
p4d_mkuptodate(*p4d);
}
} else
ret = update_pud_range(p4d, addr, next, ops);
} while (p4d++, addr = next, ((addr < end) && !ret));
return ret;
}
int um_tlb_sync(struct mm_struct *mm)
{
pgd_t *pgd;
struct vm_ops ops;
unsigned long addr, next;
int ret = 0;
guard(spinlock_irqsave)(&mm->page_table_lock);
guard(spinlock_irqsave)(&mm->context.sync_tlb_lock);
if (mm->context.sync_tlb_range_to == 0)
return 0;
ops.mm_idp = &mm->context.id;
if (mm == &init_mm) {
ops.mmap = kern_map;
ops.unmap = kern_unmap;
} else {
ops.mmap = map;
ops.unmap = unmap;
}
addr = mm->context.sync_tlb_range_from;
pgd = pgd_offset(mm, addr);
do {
next = pgd_addr_end(addr, mm->context.sync_tlb_range_to);
if (!pgd_present(*pgd)) {
if (pgd_needsync(*pgd)) {
ret = ops.unmap(ops.mm_idp, addr,
next - addr);
pgd_mkuptodate(*pgd);
}
} else
ret = update_p4d_range(pgd, addr, next, &ops);
} while (pgd++, addr = next,
((addr < mm->context.sync_tlb_range_to) && !ret));
if (ret == -ENOMEM)
report_enomem();
mm->context.sync_tlb_range_from = 0;
mm->context.sync_tlb_range_to = 0;
return ret;
}
void flush_tlb_all(void)
{
/*
* Don't bother flushing if this address space is about to be
* destroyed.
*/
if (atomic_read(&current->mm->mm_users) == 0)
return;
flush_tlb_mm(current->mm);
}
void flush_tlb_mm(struct mm_struct *mm)
{
struct vm_area_struct *vma;
VMA_ITERATOR(vmi, mm, 0);
for_each_vma(vmi, vma)
um_tlb_mark_sync(mm, vma->vm_start, vma->vm_end);
}
+430
View File
@@ -0,0 +1,430 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/mm.h>
#include <linux/sched/signal.h>
#include <linux/hardirq.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/sched/debug.h>
#include <asm/current.h>
#include <asm/tlbflush.h>
#include <arch.h>
#include <as-layout.h>
#include <kern_util.h>
#include <os.h>
#include <skas.h>
/*
* NOTE: UML does not have exception tables. As such, this is almost a copy
* of the code in mm/memory.c, only adjusting the logic to simply check whether
* we are coming from the kernel instead of doing an additional lookup in the
* exception table.
* We can do this simplification because we never get here if the exception was
* fixable.
*/
static inline bool get_mmap_lock_carefully(struct mm_struct *mm, bool is_user)
{
if (likely(mmap_read_trylock(mm)))
return true;
if (!is_user)
return false;
return !mmap_read_lock_killable(mm);
}
static inline bool mmap_upgrade_trylock(struct mm_struct *mm)
{
/*
* We don't have this operation yet.
*
* It should be easy enough to do: it's basically a
* atomic_long_try_cmpxchg_acquire()
* from RWSEM_READER_BIAS -> RWSEM_WRITER_LOCKED, but
* it also needs the proper lockdep magic etc.
*/
return false;
}
static inline bool upgrade_mmap_lock_carefully(struct mm_struct *mm, bool is_user)
{
mmap_read_unlock(mm);
if (!is_user)
return false;
return !mmap_write_lock_killable(mm);
}
/*
* Helper for page fault handling.
*
* This is kind of equivalend to "mmap_read_lock()" followed
* by "find_extend_vma()", except it's a lot more careful about
* the locking (and will drop the lock on failure).
*
* For example, if we have a kernel bug that causes a page
* fault, we don't want to just use mmap_read_lock() to get
* the mm lock, because that would deadlock if the bug were
* to happen while we're holding the mm lock for writing.
*
* So this checks the exception tables on kernel faults in
* order to only do this all for instructions that are actually
* expected to fault.
*
* We can also actually take the mm lock for writing if we
* need to extend the vma, which helps the VM layer a lot.
*/
static struct vm_area_struct *
um_lock_mm_and_find_vma(struct mm_struct *mm,
unsigned long addr, bool is_user)
{
struct vm_area_struct *vma;
if (!get_mmap_lock_carefully(mm, is_user))
return NULL;
vma = find_vma(mm, addr);
if (likely(vma && (vma->vm_start <= addr)))
return vma;
/*
* Well, dang. We might still be successful, but only
* if we can extend a vma to do so.
*/
if (!vma || !(vma->vm_flags & VM_GROWSDOWN)) {
mmap_read_unlock(mm);
return NULL;
}
/*
* We can try to upgrade the mmap lock atomically,
* in which case we can continue to use the vma
* we already looked up.
*
* Otherwise we'll have to drop the mmap lock and
* re-take it, and also look up the vma again,
* re-checking it.
*/
if (!mmap_upgrade_trylock(mm)) {
if (!upgrade_mmap_lock_carefully(mm, is_user))
return NULL;
vma = find_vma(mm, addr);
if (!vma)
goto fail;
if (vma->vm_start <= addr)
goto success;
if (!(vma->vm_flags & VM_GROWSDOWN))
goto fail;
}
if (expand_stack_locked(vma, addr))
goto fail;
success:
mmap_write_downgrade(mm);
return vma;
fail:
mmap_write_unlock(mm);
return NULL;
}
/*
* Note this is constrained to return 0, -EFAULT, -EACCES, -ENOMEM by
* segv().
*/
int handle_page_fault(unsigned long address, unsigned long ip,
int is_write, int is_user, int *code_out)
{
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
pmd_t *pmd;
pte_t *pte;
int err = -EFAULT;
unsigned int flags = FAULT_FLAG_DEFAULT;
*code_out = SEGV_MAPERR;
/*
* If the fault was with pagefaults disabled, don't take the fault, just
* fail.
*/
if (faulthandler_disabled())
goto out_nosemaphore;
if (is_user)
flags |= FAULT_FLAG_USER;
retry:
vma = um_lock_mm_and_find_vma(mm, address, is_user);
if (!vma)
goto out_nosemaphore;
*code_out = SEGV_ACCERR;
if (is_write) {
if (!(vma->vm_flags & VM_WRITE))
goto out;
flags |= FAULT_FLAG_WRITE;
} else {
/* Don't require VM_READ|VM_EXEC for write faults! */
if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
goto out;
}
do {
vm_fault_t fault;
fault = handle_mm_fault(vma, address, flags, NULL);
if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
goto out_nosemaphore;
/* The fault is fully completed (including releasing mmap lock) */
if (fault & VM_FAULT_COMPLETED)
return 0;
if (unlikely(fault & VM_FAULT_ERROR)) {
if (fault & VM_FAULT_OOM) {
goto out_of_memory;
} else if (fault & VM_FAULT_SIGSEGV) {
goto out;
} else if (fault & VM_FAULT_SIGBUS) {
err = -EACCES;
goto out;
}
BUG();
}
if (fault & VM_FAULT_RETRY) {
flags |= FAULT_FLAG_TRIED;
goto retry;
}
pmd = pmd_off(mm, address);
pte = pte_offset_kernel(pmd, address);
} while (!pte_present(*pte));
err = 0;
/*
* The below warning was added in place of
* pte_mkyoung(); if (is_write) pte_mkdirty();
* If it's triggered, we'd see normally a hang here (a clean pte is
* marked read-only to emulate the dirty bit).
* However, the generic code can mark a PTE writable but clean on a
* concurrent read fault, triggering this harmlessly. So comment it out.
*/
#if 0
WARN_ON(!pte_young(*pte) || (is_write && !pte_dirty(*pte)));
#endif
out:
mmap_read_unlock(mm);
out_nosemaphore:
return err;
out_of_memory:
/*
* We ran out of memory, call the OOM killer, and return the userspace
* (which will retry the fault, or kill us if we got oom-killed).
*/
mmap_read_unlock(mm);
if (!is_user)
goto out_nosemaphore;
pagefault_out_of_memory();
return 0;
}
static void show_segv_info(struct uml_pt_regs *regs)
{
struct task_struct *tsk = current;
struct faultinfo *fi = UPT_FAULTINFO(regs);
if (!unhandled_signal(tsk, SIGSEGV))
return;
if (!printk_ratelimit())
return;
printk("%s%s[%d]: segfault at %lx ip %px sp %px error %x",
task_pid_nr(tsk) > 1 ? KERN_INFO : KERN_EMERG,
tsk->comm, task_pid_nr(tsk), FAULT_ADDRESS(*fi),
(void *)UPT_IP(regs), (void *)UPT_SP(regs),
fi->error_code);
print_vma_addr(KERN_CONT " in ", UPT_IP(regs));
printk(KERN_CONT "\n");
}
static void bad_segv(struct faultinfo fi, unsigned long ip)
{
current->thread.arch.faultinfo = fi;
force_sig_fault(SIGSEGV, SEGV_ACCERR, (void __user *) FAULT_ADDRESS(fi));
}
void fatal_sigsegv(void)
{
force_fatal_sig(SIGSEGV);
do_signal(&current->thread.regs);
/*
* This is to tell gcc that we're not returning - do_signal
* can, in general, return, but in this case, it's not, since
* we just got a fatal SIGSEGV queued.
*/
os_dump_core();
}
/**
* segv_handler() - the SIGSEGV handler
* @sig: the signal number
* @unused_si: the signal info struct; unused in this handler
* @regs: the ptrace register information
* @mc: the mcontext of the signal
*
* The handler first extracts the faultinfo from the UML ptrace regs struct.
* If the userfault did not happen in an UML userspace process, bad_segv is called.
* Otherwise the signal did happen in a cloned userspace process, handle it.
*/
void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs,
void *mc)
{
struct faultinfo * fi = UPT_FAULTINFO(regs);
if (UPT_IS_USER(regs) && !SEGV_IS_FIXABLE(fi)) {
show_segv_info(regs);
bad_segv(*fi, UPT_IP(regs));
return;
}
segv(*fi, UPT_IP(regs), UPT_IS_USER(regs), regs, mc);
}
/*
* We give a *copy* of the faultinfo in the regs to segv.
* This must be done, since nesting SEGVs could overwrite
* the info in the regs. A pointer to the info then would
* give us bad data!
*/
unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
struct uml_pt_regs *regs, void *mc)
{
int si_code;
int err;
int is_write = FAULT_WRITE(fi);
unsigned long address = FAULT_ADDRESS(fi);
if (!is_user && regs)
current->thread.segv_regs = container_of(regs, struct pt_regs, regs);
if (!is_user && address >= start_vm && address < end_vm) {
/*
* Kernel has pending updates from set_ptes that were not
* flushed yet. Syncing them should fix the pagefault (if not
* we'll get here again and panic).
*/
err = um_tlb_sync(&init_mm);
if (err == -ENOMEM)
report_enomem();
if (err)
panic("Failed to sync kernel TLBs: %d", err);
goto out;
}
else if (current->pagefault_disabled) {
if (!mc) {
show_regs(container_of(regs, struct pt_regs, regs));
panic("Segfault with pagefaults disabled but no mcontext");
}
if (!current->thread.segv_continue) {
show_regs(container_of(regs, struct pt_regs, regs));
panic("Segfault without recovery target");
}
mc_set_rip(mc, current->thread.segv_continue);
current->thread.segv_continue = NULL;
goto out;
}
else if (current->mm == NULL) {
show_regs(container_of(regs, struct pt_regs, regs));
panic("Segfault with no mm");
}
else if (!is_user && address > PAGE_SIZE && address < TASK_SIZE) {
show_regs(container_of(regs, struct pt_regs, regs));
panic("Kernel tried to access user memory at addr 0x%lx, ip 0x%lx",
address, ip);
}
if (SEGV_IS_FIXABLE(&fi))
err = handle_page_fault(address, ip, is_write, is_user,
&si_code);
else {
err = -EFAULT;
/*
* A thread accessed NULL, we get a fault, but CR2 is invalid.
* This code is used in __do_copy_from_user() of TT mode.
* XXX tt mode is gone, so maybe this isn't needed any more
*/
address = 0;
}
if (!err)
goto out;
else if (!is_user && arch_fixup(ip, regs))
goto out;
if (!is_user) {
show_regs(container_of(regs, struct pt_regs, regs));
panic("Kernel mode fault at addr 0x%lx, ip 0x%lx",
address, ip);
}
show_segv_info(regs);
if (err == -EACCES) {
current->thread.arch.faultinfo = fi;
force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *)address);
} else {
BUG_ON(err != -EFAULT);
current->thread.arch.faultinfo = fi;
force_sig_fault(SIGSEGV, si_code, (void __user *) address);
}
out:
if (regs)
current->thread.segv_regs = NULL;
return 0;
}
void relay_signal(int sig, struct siginfo *si, struct uml_pt_regs *regs,
void *mc)
{
int code, err;
if (!UPT_IS_USER(regs)) {
if (sig == SIGBUS)
printk(KERN_ERR "Bus error - the host /dev/shm or /tmp "
"mount likely just ran out of space\n");
panic("Kernel mode signal %d", sig);
}
arch_examine_signal(sig, regs);
/* Is the signal layout for the signal known?
* Signal data must be scrubbed to prevent information leaks.
*/
code = si->si_code;
err = si->si_errno;
if ((err == 0) && (siginfo_layout(sig, code) == SIL_FAULT)) {
struct faultinfo *fi = UPT_FAULTINFO(regs);
current->thread.arch.faultinfo = *fi;
force_sig_fault(sig, code, (void __user *)FAULT_ADDRESS(*fi));
} else {
printk(KERN_ERR "Attempted to relay unknown signal %d (si_code = %d) with errno %d\n",
sig, code, err);
force_sig(sig);
}
}
void winch(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs,
void *mc)
{
do_IRQ(WINCH_IRQ, regs);
}
+551
View File
@@ -0,0 +1,551 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/cpu.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/ctype.h>
#include <linux/module.h>
#include <linux/panic_notifier.h>
#include <linux/seq_file.h>
#include <linux/string.h>
#include <linux/string_choices.h>
#include <linux/utsname.h>
#include <linux/sched.h>
#include <linux/sched/task.h>
#include <linux/kmsg_dump.h>
#include <linux/suspend.h>
#include <linux/random.h>
#include <linux/smp-internal.h>
#include <asm/processor.h>
#include <asm/cpufeature.h>
#include <asm/sections.h>
#include <asm/setup.h>
#include <asm/text-patching.h>
#include <as-layout.h>
#include <arch.h>
#include <init.h>
#include <kern.h>
#include <kern_util.h>
#include <mem_user.h>
#include <os.h>
#include "um_arch.h"
#define DEFAULT_COMMAND_LINE_ROOT "root=98:0"
#define DEFAULT_COMMAND_LINE_CONSOLE "console=tty0"
/* Changed in add_arg and setup_arch, which run before SMP is started */
static char __initdata command_line[COMMAND_LINE_SIZE] = { 0 };
static void __init add_arg(char *arg)
{
if (strlen(command_line) + strlen(arg) + 1 > COMMAND_LINE_SIZE) {
os_warn("add_arg: Too many command line arguments!\n");
exit(1);
}
if (strlen(command_line) > 0)
strcat(command_line, " ");
strcat(command_line, arg);
}
/*
* These fields are initialized at boot time and not changed.
*/
struct cpuinfo_um boot_cpu_data = {
.loops_per_jiffy = 0,
.cache_alignment = L1_CACHE_BYTES,
.x86_capability = { 0 }
};
EXPORT_SYMBOL(boot_cpu_data);
/* Changed in setup_arch, which is called in early boot */
static char host_info[(__NEW_UTS_LEN + 1) * 5];
static int show_cpuinfo(struct seq_file *m, void *v)
{
int i = 0;
#if IS_ENABLED(CONFIG_SMP)
i = (uintptr_t) v - 1;
if (!cpu_online(i))
return 0;
#endif
seq_printf(m, "processor\t: %d\n", i);
seq_printf(m, "vendor_id\t: User Mode Linux\n");
seq_printf(m, "model name\t: UML\n");
seq_printf(m, "mode\t\t: skas\n");
seq_printf(m, "host\t\t: %s\n", host_info);
seq_printf(m, "fpu\t\t: %s\n", str_yes_no(cpu_has(&boot_cpu_data, X86_FEATURE_FPU)));
seq_printf(m, "flags\t\t:");
for (i = 0; i < 32*NCAPINTS; i++)
if (cpu_has(&boot_cpu_data, i) && (x86_cap_flags[i] != NULL))
seq_printf(m, " %s", x86_cap_flags[i]);
seq_printf(m, "\n");
seq_printf(m, "cache_alignment\t: %d\n", boot_cpu_data.cache_alignment);
seq_printf(m, "bogomips\t: %lu.%02lu\n",
loops_per_jiffy/(500000/HZ),
(loops_per_jiffy/(5000/HZ)) % 100);
return 0;
}
static void *c_start(struct seq_file *m, loff_t *pos)
{
if (*pos < nr_cpu_ids)
return (void *)(uintptr_t)(*pos + 1);
return 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)
{
}
const struct seq_operations cpuinfo_op = {
.start = c_start,
.next = c_next,
.stop = c_stop,
.show = show_cpuinfo,
};
/* Set in linux_main */
unsigned long uml_physmem;
EXPORT_SYMBOL(uml_physmem);
unsigned long uml_reserved; /* Also modified in mem_init */
unsigned long start_vm;
unsigned long end_vm;
/* Set in early boot */
static int have_root __initdata;
static int have_console __initdata;
/* Set in uml_mem_setup and modified in linux_main */
unsigned long long physmem_size = 64 * 1024 * 1024;
EXPORT_SYMBOL(physmem_size);
static const char *usage_string =
"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)
{
/* Explicitly use printf() to show version in stdout */
printf("%s\n", init_utsname()->release);
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"
);
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"
);
static int __init Usage(char *line, int *add)
{
const char **p;
printf(usage_string, init_utsname()->release);
p = &__uml_help_start;
/* Explicitly use printf() to show help in stdout */
while (p < &__uml_help_end) {
printf("%s", *p);
p++;
}
exit(0);
return 0;
}
__uml_setup("--help", Usage,
"--help\n"
" Prints this message.\n\n"
);
static void __init uml_checksetup(char *line, int *add)
{
struct uml_param *p;
p = &__uml_setup_start;
while (p < &__uml_setup_end) {
size_t n;
n = strlen(p->str);
if (!strncmp(line, p->str, n) && p->setup_func(line + n, add))
return;
p++;
}
}
static void __init uml_postsetup(void)
{
initcall_t *p;
p = &__uml_postsetup_start;
while (p < &__uml_postsetup_end) {
(*p)();
p++;
}
return;
}
static int panic_exit(struct notifier_block *self, unsigned long unused1,
void *unused2)
{
kmsg_dump(KMSG_DUMP_PANIC);
bust_spinlocks(1);
bust_spinlocks(0);
uml_exitcode = 1;
os_dump_core();
return NOTIFY_DONE;
}
static struct notifier_block panic_exit_notifier = {
.notifier_call = panic_exit,
.priority = INT_MAX - 1, /* run as 2nd notifier, won't return */
};
void uml_finishsetup(void)
{
atomic_notifier_chain_register(&panic_notifier_list,
&panic_exit_notifier);
uml_postsetup();
new_thread_handler();
}
/* Set during early boot */
unsigned long stub_start;
unsigned long task_size;
EXPORT_SYMBOL(task_size);
unsigned long brk_start;
#define MIN_VMALLOC (32 * 1024 * 1024)
static void __init parse_host_cpu_flags(char *line)
{
int i;
for (i = 0; i < 32*NCAPINTS; i++) {
if ((x86_cap_flags[i] != NULL) && strstr(line, x86_cap_flags[i]))
set_cpu_cap(&boot_cpu_data, i);
}
}
static void __init parse_cache_line(char *line)
{
long res;
char *to_parse = strstr(line, ":");
if (to_parse) {
to_parse++;
while (*to_parse != 0 && isspace(*to_parse)) {
to_parse++;
}
if (kstrtoul(to_parse, 10, &res) == 0 && is_power_of_2(res))
boot_cpu_data.cache_alignment = res;
else
boot_cpu_data.cache_alignment = L1_CACHE_BYTES;
}
}
static unsigned long __init get_top_address(char **envp)
{
unsigned long top_addr = (unsigned long) &top_addr;
int i;
/* The earliest variable should be after the program name in ELF */
for (i = 0; envp[i]; i++) {
if ((unsigned long) envp[i] > top_addr)
top_addr = (unsigned long) envp[i];
}
return PAGE_ALIGN(top_addr + 1);
}
int __init linux_main(int argc, char **argv, char **envp)
{
unsigned long avail, diff;
unsigned long virtmem_size, max_physmem;
unsigned long host_task_size;
unsigned long stack;
unsigned int i;
int add;
for (i = 1; i < argc; i++) {
if ((i == 1) && (argv[i][0] == ' '))
continue;
add = 1;
uml_checksetup(argv[i], &add);
if (add)
add_arg(argv[i]);
}
if (have_root == 0)
add_arg(DEFAULT_COMMAND_LINE_ROOT);
if (have_console == 0)
add_arg(DEFAULT_COMMAND_LINE_CONSOLE);
host_task_size = get_top_address(envp);
/* reserve a few pages for the stubs */
stub_start = host_task_size - STUB_SIZE;
host_task_size = stub_start;
/* Limit TASK_SIZE to what is addressable by the page table */
task_size = host_task_size;
if (task_size > (unsigned long long) PTRS_PER_PGD * PGDIR_SIZE)
task_size = PTRS_PER_PGD * PGDIR_SIZE;
/*
* TASK_SIZE needs to be PGDIR_SIZE aligned or else exit_mmap craps
* out
*/
task_size = task_size & PGDIR_MASK;
/* OS sanity checks that need to happen before the kernel runs */
os_early_checks();
get_host_cpu_features(parse_host_cpu_flags, parse_cache_line);
brk_start = (unsigned long) sbrk(0);
/*
* 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
*/
diff = PAGE_ALIGN(brk_start) - PAGE_ALIGN((unsigned long) &_end);
if (diff > 1024 * 1024) {
os_info("Adding %ld bytes to physical memory to account for "
"exec-shield gap\n", diff);
physmem_size += diff;
}
uml_physmem = (unsigned long) __binary_start & PAGE_MASK;
/* Reserve up to 4M after the current brk */
uml_reserved = ROUND_4M(brk_start) + (1 << 22);
setup_machinename(init_utsname()->machine);
physmem_size = PAGE_ALIGN(physmem_size);
max_physmem = TASK_SIZE - uml_physmem - MIN_VMALLOC;
if (physmem_size > max_physmem) {
physmem_size = max_physmem;
os_info("Physical memory size shrunk to %llu bytes\n",
physmem_size);
}
high_physmem = uml_physmem + physmem_size;
start_vm = VMALLOC_START;
virtmem_size = physmem_size;
stack = (unsigned long) argv;
stack &= ~(1024 * 1024 - 1);
avail = stack - start_vm;
if (physmem_size > avail)
virtmem_size = avail;
end_vm = start_vm + virtmem_size;
if (virtmem_size < physmem_size)
os_info("Kernel virtual memory size shrunk to %lu bytes\n",
virtmem_size);
arch_task_struct_size = sizeof(struct task_struct) + host_fp_size;
os_flush_stdout();
return start_uml();
}
int __init __weak read_initrd(void)
{
return 0;
}
void __init setup_arch(char **cmdline_p)
{
u8 rng_seed[32];
stack_protections((unsigned long) init_task.stack);
setup_physmem(uml_physmem, uml_reserved, physmem_size);
uml_dtb_init();
read_initrd();
strscpy(boot_command_line, command_line, COMMAND_LINE_SIZE);
*cmdline_p = command_line;
setup_hostinfo(host_info, sizeof host_info);
prefill_possible_map();
if (os_getrandom(rng_seed, sizeof(rng_seed), 0) == sizeof(rng_seed)) {
add_bootloader_randomness(rng_seed, sizeof(rng_seed));
memzero_explicit(rng_seed, sizeof(rng_seed));
}
}
void __init arch_cpu_finalize_init(void)
{
arch_check_bugs();
os_check_bugs();
}
void apply_seal_endbr(s32 *start, s32 *end)
{
}
void apply_retpolines(s32 *start, s32 *end)
{
}
void apply_returns(s32 *start, s32 *end)
{
}
void apply_fineibt(s32 *start_retpoline, s32 *end_retpoline,
s32 *start_cfi, s32 *end_cfi)
{
}
void apply_alternatives(struct alt_instr *start, struct alt_instr *end)
{
}
#if IS_ENABLED(CONFIG_SMP)
void alternatives_smp_module_add(struct module *mod, char *name,
void *locks, void *locks_end,
void *text, void *text_end)
{
}
void alternatives_smp_module_del(struct module *mod)
{
}
#endif
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_copy(void *addr, const void *opcode, size_t len)
{
return text_poke(addr, opcode, len);
}
void smp_text_poke_sync_each_cpu(void)
{
}
void uml_pm_wake(void)
{
pm_system_wakeup();
}
#ifdef CONFIG_PM_SLEEP
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,
};
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();
suspend_set_ops(&um_suspend_ops);
return 0;
}
late_initcall(init_pm_wake_signal);
#endif
+16
View File
@@ -0,0 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __UML_ARCH_H__
#define __UML_ARCH_H__
extern void * __init uml_load_file(const char *filename, unsigned long long *size);
#ifdef CONFIG_OF
extern void __init uml_dtb_init(void);
#else
static inline void uml_dtb_init(void) { }
#endif
extern int __init read_initrd(void);
#endif
+38
View File
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <asm/errno.h>
#include <init.h>
#include <kern.h>
#include <os.h>
/* Changed by set_umid_arg */
static int umid_inited;
static int __init set_umid_arg(char *name, int *add)
{
int err;
if (umid_inited) {
os_warn("umid already set\n");
return 0;
}
*add = 0;
err = set_umid(name);
if (err == -EEXIST)
os_warn("umid '%s' already in use\n", name);
else if (!err)
umid_inited = 1;
return 0;
}
__uml_setup("umid=", set_umid_arg,
"umid=<name>\n"
" This is used to assign a unique identity to this UML machine and\n"
" is used for naming the pid file and management console socket.\n\n"
);
+120
View File
@@ -0,0 +1,120 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <asm/vmlinux.lds.h>
#include <asm/page.h>
OUTPUT_FORMAT(ELF_FORMAT)
OUTPUT_ARCH(ELF_ARCH)
ENTRY(_start)
jiffies = jiffies_64;
VERSION {
{
local: *;
};
}
SECTIONS
{
/* This must contain the right address - not quite the default ELF one.*/
PROVIDE (__executable_start = START);
/* Static binaries stick stuff here, like the sigreturn trampoline,
* invisibly to objdump. So, just make __binary_start equal to the very
* beginning of the executable, and if there are unmapped pages after this,
* they are forever unusable.
*/
__binary_start = START;
. = START + SIZEOF_HEADERS;
. = ALIGN(PAGE_SIZE);
_text = .;
INIT_TEXT_SECTION(0)
.text :
{
_stext = .;
TEXT_TEXT
SCHED_TEXT
LOCK_TEXT
IRQENTRY_TEXT
SOFTIRQENTRY_TEXT
*(.fixup)
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
*(.gnu.linkonce.t*)
}
. = ALIGN(PAGE_SIZE);
.syscall_stub : {
__syscall_stub_start = .;
*(.__syscall_stub*)
__syscall_stub_end = .;
}
/*
* These are needed even in a static link, even if they wind up being empty.
* Newer glibc needs these __rel{,a}_iplt_{start,end} symbols.
*/
.rel.plt : {
*(.rel.plt)
PROVIDE_HIDDEN(__rel_iplt_start = .);
*(.rel.iplt)
PROVIDE_HIDDEN(__rel_iplt_end = .);
}
.rela.plt : {
*(.rela.plt)
PROVIDE_HIDDEN(__rela_iplt_start = .);
*(.rela.iplt)
PROVIDE_HIDDEN(__rela_iplt_end = .);
}
#include <asm/common.lds.S>
__init_begin = .;
init.data : { INIT_DATA }
__init_end = .;
.data :
{
INIT_TASK_DATA(KERNEL_STACK_SIZE)
DATA_DATA
*(.gnu.linkonce.d*)
CONSTRUCTORS
}
.data1 : { *(.data1) }
.ctors :
{
*(.ctors)
}
.dtors :
{
*(.dtors)
}
.got : { *(.got.plt) *(.got) }
.eh_frame : { KEEP (*(.eh_frame)) }
.dynamic : { *(.dynamic) }
.tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
/* We want the small data sections together, so single-instruction offsets
can access them all, and initialized data all before uninitialized, so
we can shorten the on-disk segment size. */
.sdata : { *(.sdata) }
_edata = .;
PROVIDE (edata = .);
. = ALIGN(PAGE_SIZE);
__bss_start = .;
PROVIDE(_bss_start = .);
SBSS(0)
BSS(0)
__bss_stop = .;
_end = .;
PROVIDE (end = .);
STABS_DEBUG
DWARF_DEBUG
MODINFO
ELF_DETAILS
DISCARDS
}
+8
View File
@@ -0,0 +1,8 @@
#define RUNTIME_DISCARD_EXIT
KERNEL_STACK_SIZE = 4096 * (1 << CONFIG_KERNEL_STACK_ORDER);
#ifdef CONFIG_LD_SCRIPT_STATIC
#include "uml.lds.S"
#else
#include "dyn.lds.S"
#endif