Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
47
arch/um/kernel/Makefile
Normal file
47
arch/um/kernel/Makefile
Normal file
@@ -0,0 +1,47 @@
|
||||
#
|
||||
# Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
# Licensed under the GPL
|
||||
#
|
||||
|
||||
extra-y := vmlinux.lds
|
||||
clean-files :=
|
||||
|
||||
obj-y = config.o exec.o exitcode.o init_task.o irq.o ksyms.o mem.o \
|
||||
physmem.o process.o ptrace.o reboot.o sigio.o \
|
||||
signal.o smp.o syscall.o sysrq.o time.o tlb.o trap.o uaccess.o \
|
||||
um_arch.o umid.o
|
||||
|
||||
obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o
|
||||
obj-$(CONFIG_GPROF) += gprof_syms.o
|
||||
obj-$(CONFIG_GCOV) += gmon_syms.o
|
||||
|
||||
obj-$(CONFIG_MODE_TT) += tt/
|
||||
obj-$(CONFIG_MODE_SKAS) += skas/
|
||||
|
||||
USER_OBJS := config.o
|
||||
|
||||
include arch/um/scripts/Makefile.rules
|
||||
|
||||
targets := config.c config.tmp
|
||||
|
||||
# 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_quote2 = QUOTE $@
|
||||
cmd_quote2 = sed -e '/CONFIG/{' \
|
||||
-e 's/"CONFIG"\;/""/' \
|
||||
-e 'r $(obj)/config.tmp' \
|
||||
-e 'a \' \
|
||||
-e '""\;' \
|
||||
-e '}' \
|
||||
$< > $@
|
||||
1
arch/um/kernel/asm-offsets.c
Normal file
1
arch/um/kernel/asm-offsets.c
Normal file
@@ -0,0 +1 @@
|
||||
#include "sysdep/kernel-offsets.h"
|
||||
32
arch/um/kernel/config.c.in
Normal file
32
arch/um/kernel/config.c.in
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "init.h"
|
||||
|
||||
static __initdata char *config = "CONFIG";
|
||||
|
||||
static int __init print_config(char *line, int *add)
|
||||
{
|
||||
printf("%s", config);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
__uml_setup("--showconfig", print_config,
|
||||
"--showconfig\n"
|
||||
" Prints the config file that this UML binary was generated from.\n\n"
|
||||
);
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
155
arch/um/kernel/dyn.lds.S
Normal file
155
arch/um/kernel/dyn.lds.S
Normal file
@@ -0,0 +1,155 @@
|
||||
#include <asm-generic/vmlinux.lds.h>
|
||||
|
||||
OUTPUT_FORMAT(ELF_FORMAT)
|
||||
OUTPUT_ARCH(ELF_ARCH)
|
||||
ENTRY(_start)
|
||||
jiffies = jiffies_64;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
PROVIDE (__executable_start = START);
|
||||
. = START + SIZEOF_HEADERS;
|
||||
.interp : { *(.interp) }
|
||||
/* Used in arch/um/kernel/mem.c. Any memory between START and __binary_start
|
||||
* is remapped.*/
|
||||
__binary_start = .;
|
||||
. = ALIGN(4096); /* Init code and data */
|
||||
_text = .;
|
||||
_stext = .;
|
||||
__init_begin = .;
|
||||
.init.text : {
|
||||
_sinittext = .;
|
||||
*(.init.text)
|
||||
_einittext = .;
|
||||
}
|
||||
|
||||
. = ALIGN(4096);
|
||||
|
||||
/* 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) }
|
||||
.rela.plt : { *(.rela.plt) }
|
||||
.init : {
|
||||
KEEP (*(.init))
|
||||
} =0x90909090
|
||||
.plt : { *(.plt) }
|
||||
.text : {
|
||||
*(.text)
|
||||
SCHED_TEXT
|
||||
LOCK_TEXT
|
||||
*(.fixup)
|
||||
*(.stub .text.* .gnu.linkonce.t.*)
|
||||
/* .gnu.warning sections are handled specially by elf32.em. */
|
||||
*(.gnu.warning)
|
||||
|
||||
. = ALIGN(4096);
|
||||
__syscall_stub_start = .;
|
||||
*(.__syscall_stub*)
|
||||
__syscall_stub_end = .;
|
||||
. = ALIGN(4096);
|
||||
} =0x90909090
|
||||
.fini : {
|
||||
KEEP (*(.fini))
|
||||
} =0x90909090
|
||||
|
||||
.kstrtab : { *(.kstrtab) }
|
||||
|
||||
#include "asm/common.lds.S"
|
||||
|
||||
init.data : { *(.init.data) }
|
||||
|
||||
/* 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 : { *(.init_array) }
|
||||
.fini_array : { *(.fini_array) }
|
||||
.data : {
|
||||
. = ALIGN(KERNEL_STACK_SIZE); /* init_task */
|
||||
*(.data.init_task)
|
||||
*(.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_start = .;
|
||||
.bss : {
|
||||
*(.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);
|
||||
}
|
||||
_end = .;
|
||||
PROVIDE (end = .);
|
||||
|
||||
STABS_DEBUG
|
||||
|
||||
DWARF_DEBUG
|
||||
}
|
||||
89
arch/um/kernel/exec.c
Normal file
89
arch/um/kernel/exec.c
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/slab.h"
|
||||
#include "linux/smp_lock.h"
|
||||
#include "linux/ptrace.h"
|
||||
#include "asm/ptrace.h"
|
||||
#include "asm/pgtable.h"
|
||||
#include "asm/tlbflush.h"
|
||||
#include "asm/uaccess.h"
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "mem_user.h"
|
||||
#include "kern.h"
|
||||
#include "irq_user.h"
|
||||
#include "tlb.h"
|
||||
#include "os.h"
|
||||
#include "choose-mode.h"
|
||||
#include "mode_kern.h"
|
||||
|
||||
void flush_thread(void)
|
||||
{
|
||||
arch_flush_thread(¤t->thread.arch);
|
||||
CHOOSE_MODE(flush_thread_tt(), flush_thread_skas());
|
||||
}
|
||||
|
||||
void start_thread(struct pt_regs *regs, unsigned long eip, unsigned long esp)
|
||||
{
|
||||
CHOOSE_MODE_PROC(start_thread_tt, start_thread_skas, regs, eip, esp);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TTY_LOG
|
||||
extern void log_exec(char **argv, void *tty);
|
||||
#endif
|
||||
|
||||
static long execve1(char *file, char __user * __user *argv,
|
||||
char __user *__user *env)
|
||||
{
|
||||
long error;
|
||||
#ifdef CONFIG_TTY_LOG
|
||||
struct tty_struct *tty;
|
||||
|
||||
mutex_lock(&tty_mutex);
|
||||
tty = get_current_tty();
|
||||
if (tty)
|
||||
log_exec(argv, tty);
|
||||
mutex_unlock(&tty_mutex);
|
||||
#endif
|
||||
error = do_execve(file, argv, env, ¤t->thread.regs);
|
||||
if (error == 0){
|
||||
task_lock(current);
|
||||
current->ptrace &= ~PT_DTRACE;
|
||||
#ifdef SUBARCH_EXECVE1
|
||||
SUBARCH_EXECVE1(¤t->thread.regs.regs);
|
||||
#endif
|
||||
task_unlock(current);
|
||||
set_cmdline(current_cmd());
|
||||
}
|
||||
return(error);
|
||||
}
|
||||
|
||||
long um_execve(char *file, char __user *__user *argv, char __user *__user *env)
|
||||
{
|
||||
long err;
|
||||
|
||||
err = execve1(file, argv, env);
|
||||
if(!err)
|
||||
do_longjmp(current->thread.exec_buf, 1);
|
||||
return(err);
|
||||
}
|
||||
|
||||
long sys_execve(char __user *file, char __user *__user *argv,
|
||||
char __user *__user *env)
|
||||
{
|
||||
long error;
|
||||
char *filename;
|
||||
|
||||
lock_kernel();
|
||||
filename = getname(file);
|
||||
error = PTR_ERR(filename);
|
||||
if (IS_ERR(filename)) goto out;
|
||||
error = execve1(filename, argv, env);
|
||||
putname(filename);
|
||||
out:
|
||||
unlock_kernel();
|
||||
return(error);
|
||||
}
|
||||
77
arch/um/kernel/exitcode.c
Normal file
77
arch/um/kernel/exitcode.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/init.h"
|
||||
#include "linux/ctype.h"
|
||||
#include "linux/proc_fs.h"
|
||||
#include "asm/uaccess.h"
|
||||
|
||||
/* If read and write race, the read will still atomically read a valid
|
||||
* value.
|
||||
*/
|
||||
int uml_exitcode = 0;
|
||||
|
||||
static int read_proc_exitcode(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
int len, val;
|
||||
|
||||
/* Save uml_exitcode in a local so that we don't need to guarantee
|
||||
* that sprintf accesses it atomically.
|
||||
*/
|
||||
val = uml_exitcode;
|
||||
len = sprintf(page, "%d\n", val);
|
||||
len -= off;
|
||||
if(len <= off+count) *eof = 1;
|
||||
*start = page + off;
|
||||
if(len > count) len = count;
|
||||
if(len < 0) len = 0;
|
||||
return(len);
|
||||
}
|
||||
|
||||
static int write_proc_exitcode(struct file *file, const char __user *buffer,
|
||||
unsigned long count, void *data)
|
||||
{
|
||||
char *end, buf[sizeof("nnnnn\0")];
|
||||
int tmp;
|
||||
|
||||
if(copy_from_user(buf, buffer, count))
|
||||
return(-EFAULT);
|
||||
tmp = simple_strtol(buf, &end, 0);
|
||||
if((*end != '\0') && !isspace(*end))
|
||||
return(-EINVAL);
|
||||
uml_exitcode = tmp;
|
||||
return(count);
|
||||
}
|
||||
|
||||
static int make_proc_exitcode(void)
|
||||
{
|
||||
struct proc_dir_entry *ent;
|
||||
|
||||
ent = create_proc_entry("exitcode", 0600, &proc_root);
|
||||
if(ent == NULL){
|
||||
printk(KERN_WARNING "make_proc_exitcode : Failed to register "
|
||||
"/proc/exitcode\n");
|
||||
return(0);
|
||||
}
|
||||
|
||||
ent->read_proc = read_proc_exitcode;
|
||||
ent->write_proc = write_proc_exitcode;
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
__initcall(make_proc_exitcode);
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
23
arch/um/kernel/gmon_syms.c
Normal file
23
arch/um/kernel/gmon_syms.c
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/module.h"
|
||||
|
||||
extern void __bb_init_func(void *) __attribute__((weak));
|
||||
EXPORT_SYMBOL(__bb_init_func);
|
||||
|
||||
/* This is defined (and referred to in profiling stub code) only by some GCC
|
||||
* versions in libgcov.
|
||||
*
|
||||
* Since SuSE backported the fix, we cannot handle it depending on GCC version.
|
||||
* So, unconditinally export it. But also give it a weak declaration, which will
|
||||
* be overriden by any other one.
|
||||
*/
|
||||
|
||||
extern void __gcov_init(void *) __attribute__((weak));
|
||||
EXPORT_SYMBOL(__gcov_init);
|
||||
|
||||
extern void __gcov_merge_add(void *) __attribute__((weak));
|
||||
EXPORT_SYMBOL(__gcov_merge_add);
|
||||
20
arch/um/kernel/gprof_syms.c
Normal file
20
arch/um/kernel/gprof_syms.c
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/module.h"
|
||||
|
||||
extern void mcount(void);
|
||||
EXPORT_SYMBOL(mcount);
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
61
arch/um/kernel/init_task.c
Normal file
61
arch/um/kernel/init_task.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/mm.h"
|
||||
#include "linux/module.h"
|
||||
#include "linux/sched.h"
|
||||
#include "linux/init_task.h"
|
||||
#include "linux/mqueue.h"
|
||||
#include "asm/uaccess.h"
|
||||
#include "asm/pgtable.h"
|
||||
#include "user_util.h"
|
||||
#include "mem_user.h"
|
||||
#include "os.h"
|
||||
|
||||
static struct fs_struct init_fs = INIT_FS;
|
||||
struct mm_struct init_mm = INIT_MM(init_mm);
|
||||
static struct files_struct init_files = INIT_FILES;
|
||||
static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
|
||||
static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
|
||||
EXPORT_SYMBOL(init_mm);
|
||||
|
||||
/*
|
||||
* Initial task structure.
|
||||
*
|
||||
* All other task structs will be allocated on slabs in fork.c
|
||||
*/
|
||||
|
||||
struct task_struct init_task = INIT_TASK(init_task);
|
||||
|
||||
EXPORT_SYMBOL(init_task);
|
||||
|
||||
/*
|
||||
* Initial thread structure.
|
||||
*
|
||||
* We need to make sure that this is 16384-byte aligned due to the
|
||||
* way process stacks are handled. This is done by having a special
|
||||
* "init_task" linker map entry..
|
||||
*/
|
||||
|
||||
union thread_union init_thread_union
|
||||
__attribute__((__section__(".data.init_task"))) =
|
||||
{ INIT_THREAD_INFO(init_task) };
|
||||
|
||||
void unprotect_stack(unsigned long stack)
|
||||
{
|
||||
os_protect_memory((void *) stack, (1 << CONFIG_KERNEL_STACK_ORDER) * PAGE_SIZE,
|
||||
1, 1, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
78
arch/um/kernel/initrd.c
Normal file
78
arch/um/kernel/initrd.c
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/init.h"
|
||||
#include "linux/bootmem.h"
|
||||
#include "linux/initrd.h"
|
||||
#include "asm/types.h"
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "initrd.h"
|
||||
#include "init.h"
|
||||
#include "os.h"
|
||||
|
||||
/* Changed by uml_initrd_setup, which is a setup */
|
||||
static char *initrd __initdata = NULL;
|
||||
|
||||
static int __init read_initrd(void)
|
||||
{
|
||||
void *area;
|
||||
long long size;
|
||||
int err;
|
||||
|
||||
if(initrd == NULL) return 0;
|
||||
err = os_file_size(initrd, &size);
|
||||
if(err) return 0;
|
||||
area = alloc_bootmem(size);
|
||||
if(area == NULL) return 0;
|
||||
if(load_initrd(initrd, area, size) == -1) return 0;
|
||||
initrd_start = (unsigned long) area;
|
||||
initrd_end = initrd_start + size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__uml_postsetup(read_initrd);
|
||||
|
||||
static int __init uml_initrd_setup(char *line, int *add)
|
||||
{
|
||||
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"
|
||||
);
|
||||
|
||||
int load_initrd(char *filename, void *buf, int size)
|
||||
{
|
||||
int fd, n;
|
||||
|
||||
fd = os_open_file(filename, of_read(OPENFLAGS()), 0);
|
||||
if(fd < 0){
|
||||
printk("Opening '%s' failed - err = %d\n", filename, -fd);
|
||||
return(-1);
|
||||
}
|
||||
n = os_read_file(fd, buf, size);
|
||||
if(n != size){
|
||||
printk("Read of %d bytes from '%s' failed, err = %d\n", size,
|
||||
filename, -n);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
os_close_file(fd);
|
||||
return(0);
|
||||
}
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
456
arch/um/kernel/irq.c
Normal file
456
arch/um/kernel/irq.c
Normal file
@@ -0,0 +1,456 @@
|
||||
/*
|
||||
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
* Derived (i.e. mostly copied) from arch/i386/kernel/irq.c:
|
||||
* Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar
|
||||
*/
|
||||
|
||||
#include "linux/kernel.h"
|
||||
#include "linux/module.h"
|
||||
#include "linux/smp.h"
|
||||
#include "linux/kernel_stat.h"
|
||||
#include "linux/interrupt.h"
|
||||
#include "linux/random.h"
|
||||
#include "linux/slab.h"
|
||||
#include "linux/file.h"
|
||||
#include "linux/proc_fs.h"
|
||||
#include "linux/init.h"
|
||||
#include "linux/seq_file.h"
|
||||
#include "linux/profile.h"
|
||||
#include "linux/hardirq.h"
|
||||
#include "asm/irq.h"
|
||||
#include "asm/hw_irq.h"
|
||||
#include "asm/atomic.h"
|
||||
#include "asm/signal.h"
|
||||
#include "asm/system.h"
|
||||
#include "asm/errno.h"
|
||||
#include "asm/uaccess.h"
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "irq_user.h"
|
||||
#include "irq_kern.h"
|
||||
#include "os.h"
|
||||
#include "sigio.h"
|
||||
#include "um_malloc.h"
|
||||
#include "misc_constants.h"
|
||||
|
||||
/*
|
||||
* Generic, controller-independent functions:
|
||||
*/
|
||||
|
||||
int show_interrupts(struct seq_file *p, void *v)
|
||||
{
|
||||
int i = *(loff_t *) v, j;
|
||||
struct irqaction * action;
|
||||
unsigned long flags;
|
||||
|
||||
if (i == 0) {
|
||||
seq_printf(p, " ");
|
||||
for_each_online_cpu(j)
|
||||
seq_printf(p, "CPU%d ",j);
|
||||
seq_putc(p, '\n');
|
||||
}
|
||||
|
||||
if (i < NR_IRQS) {
|
||||
spin_lock_irqsave(&irq_desc[i].lock, flags);
|
||||
action = irq_desc[i].action;
|
||||
if (!action)
|
||||
goto skip;
|
||||
seq_printf(p, "%3d: ",i);
|
||||
#ifndef CONFIG_SMP
|
||||
seq_printf(p, "%10u ", kstat_irqs(i));
|
||||
#else
|
||||
for_each_online_cpu(j)
|
||||
seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]);
|
||||
#endif
|
||||
seq_printf(p, " %14s", irq_desc[i].chip->typename);
|
||||
seq_printf(p, " %s", action->name);
|
||||
|
||||
for (action=action->next; action; action = action->next)
|
||||
seq_printf(p, ", %s", action->name);
|
||||
|
||||
seq_putc(p, '\n');
|
||||
skip:
|
||||
spin_unlock_irqrestore(&irq_desc[i].lock, flags);
|
||||
} else if (i == NR_IRQS) {
|
||||
seq_putc(p, '\n');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_fd *active_fds = NULL;
|
||||
static struct irq_fd **last_irq_ptr = &active_fds;
|
||||
|
||||
extern void free_irqs(void);
|
||||
|
||||
void sigio_handler(int sig, union uml_pt_regs *regs)
|
||||
{
|
||||
struct irq_fd *irq_fd;
|
||||
int n;
|
||||
|
||||
if (smp_sigio_handler())
|
||||
return;
|
||||
|
||||
while (1) {
|
||||
n = os_waiting_for_events(active_fds);
|
||||
if (n <= 0) {
|
||||
if(n == -EINTR) continue;
|
||||
else break;
|
||||
}
|
||||
|
||||
for (irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next) {
|
||||
if (irq_fd->current_events != 0) {
|
||||
irq_fd->current_events = 0;
|
||||
do_IRQ(irq_fd->irq, regs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free_irqs();
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(irq_lock);
|
||||
|
||||
int activate_fd(int irq, int fd, int type, void *dev_id)
|
||||
{
|
||||
struct pollfd *tmp_pfd;
|
||||
struct irq_fd *new_fd, *irq_fd;
|
||||
unsigned long flags;
|
||||
int pid, events, err, n;
|
||||
|
||||
pid = os_getpid();
|
||||
err = os_set_fd_async(fd, pid);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = -ENOMEM;
|
||||
new_fd = kmalloc(sizeof(struct irq_fd), GFP_KERNEL);
|
||||
if (new_fd == NULL)
|
||||
goto out;
|
||||
|
||||
if (type == IRQ_READ)
|
||||
events = UM_POLLIN | UM_POLLPRI;
|
||||
else
|
||||
events = UM_POLLOUT;
|
||||
*new_fd = ((struct irq_fd) { .next = NULL,
|
||||
.id = dev_id,
|
||||
.fd = fd,
|
||||
.type = type,
|
||||
.irq = irq,
|
||||
.pid = pid,
|
||||
.events = events,
|
||||
.current_events = 0 } );
|
||||
|
||||
err = -EBUSY;
|
||||
spin_lock_irqsave(&irq_lock, flags);
|
||||
for (irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next) {
|
||||
if ((irq_fd->fd == fd) && (irq_fd->type == type)) {
|
||||
printk("Registering fd %d twice\n", fd);
|
||||
printk("Irqs : %d, %d\n", irq_fd->irq, irq);
|
||||
printk("Ids : 0x%p, 0x%p\n", irq_fd->id, dev_id);
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == IRQ_WRITE)
|
||||
fd = -1;
|
||||
|
||||
tmp_pfd = NULL;
|
||||
n = 0;
|
||||
|
||||
while (1) {
|
||||
n = os_create_pollfd(fd, events, tmp_pfd, n);
|
||||
if (n == 0)
|
||||
break;
|
||||
|
||||
/* n > 0
|
||||
* It means we couldn't put new pollfd to current pollfds
|
||||
* and tmp_fds is NULL or too small for new pollfds array.
|
||||
* Needed size is equal to n as minimum.
|
||||
*
|
||||
* Here we have to drop the lock in order to call
|
||||
* kmalloc, which might sleep.
|
||||
* If something else came in and changed the pollfds array
|
||||
* so we will not be able to put new pollfd struct to pollfds
|
||||
* then we free the buffer tmp_fds and try again.
|
||||
*/
|
||||
spin_unlock_irqrestore(&irq_lock, flags);
|
||||
kfree(tmp_pfd);
|
||||
|
||||
tmp_pfd = kmalloc(n, GFP_KERNEL);
|
||||
if (tmp_pfd == NULL)
|
||||
goto out_kfree;
|
||||
|
||||
spin_lock_irqsave(&irq_lock, flags);
|
||||
}
|
||||
|
||||
*last_irq_ptr = new_fd;
|
||||
last_irq_ptr = &new_fd->next;
|
||||
|
||||
spin_unlock_irqrestore(&irq_lock, flags);
|
||||
|
||||
/* This calls activate_fd, so it has to be outside the critical
|
||||
* section.
|
||||
*/
|
||||
maybe_sigio_broken(fd, (type == IRQ_READ));
|
||||
|
||||
return 0;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_irqrestore(&irq_lock, flags);
|
||||
out_kfree:
|
||||
kfree(new_fd);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&irq_lock, flags);
|
||||
os_free_irq_by_cb(test, arg, active_fds, &last_irq_ptr);
|
||||
spin_unlock_irqrestore(&irq_lock, flags);
|
||||
}
|
||||
|
||||
struct irq_and_dev {
|
||||
int irq;
|
||||
void *dev;
|
||||
};
|
||||
|
||||
static int same_irq_and_dev(struct irq_fd *irq, void *d)
|
||||
{
|
||||
struct irq_and_dev *data = d;
|
||||
|
||||
return ((irq->irq == data->irq) && (irq->id == data->dev));
|
||||
}
|
||||
|
||||
void free_irq_by_irq_and_dev(unsigned int irq, void *dev)
|
||||
{
|
||||
struct irq_and_dev data = ((struct irq_and_dev) { .irq = irq,
|
||||
.dev = dev });
|
||||
|
||||
free_irq_by_cb(same_irq_and_dev, &data);
|
||||
}
|
||||
|
||||
static int same_fd(struct irq_fd *irq, void *fd)
|
||||
{
|
||||
return (irq->fd == *((int *)fd));
|
||||
}
|
||||
|
||||
void free_irq_by_fd(int fd)
|
||||
{
|
||||
free_irq_by_cb(same_fd, &fd);
|
||||
}
|
||||
|
||||
static struct irq_fd *find_irq_by_fd(int fd, int irqnum, int *index_out)
|
||||
{
|
||||
struct irq_fd *irq;
|
||||
int i = 0;
|
||||
int fdi;
|
||||
|
||||
for (irq = active_fds; irq != NULL; irq = irq->next) {
|
||||
if ((irq->fd == fd) && (irq->irq == irqnum))
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
if (irq == NULL) {
|
||||
printk("find_irq_by_fd doesn't have descriptor %d\n", fd);
|
||||
goto out;
|
||||
}
|
||||
fdi = os_get_pollfd(i);
|
||||
if ((fdi != -1) && (fdi != fd)) {
|
||||
printk("find_irq_by_fd - mismatch between active_fds and "
|
||||
"pollfds, fd %d vs %d, need %d\n", irq->fd,
|
||||
fdi, fd);
|
||||
irq = NULL;
|
||||
goto out;
|
||||
}
|
||||
*index_out = i;
|
||||
out:
|
||||
return irq;
|
||||
}
|
||||
|
||||
void reactivate_fd(int fd, int irqnum)
|
||||
{
|
||||
struct irq_fd *irq;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&irq_lock, flags);
|
||||
irq = find_irq_by_fd(fd, irqnum, &i);
|
||||
if (irq == NULL) {
|
||||
spin_unlock_irqrestore(&irq_lock, flags);
|
||||
return;
|
||||
}
|
||||
os_set_pollfd(i, irq->fd);
|
||||
spin_unlock_irqrestore(&irq_lock, flags);
|
||||
|
||||
add_sigio_fd(fd);
|
||||
}
|
||||
|
||||
void deactivate_fd(int fd, int irqnum)
|
||||
{
|
||||
struct irq_fd *irq;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&irq_lock, flags);
|
||||
irq = find_irq_by_fd(fd, irqnum, &i);
|
||||
if(irq == NULL){
|
||||
spin_unlock_irqrestore(&irq_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
os_set_pollfd(i, -1);
|
||||
spin_unlock_irqrestore(&irq_lock, flags);
|
||||
|
||||
ignore_sigio_fd(fd);
|
||||
}
|
||||
|
||||
int deactivate_all_fds(void)
|
||||
{
|
||||
struct irq_fd *irq;
|
||||
int err;
|
||||
|
||||
for (irq = active_fds; irq != NULL; irq = irq->next) {
|
||||
err = os_clear_fd_async(irq->fd);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
/* If there is a signal already queued, after unblocking ignore it */
|
||||
os_set_ioignore();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MODE_TT
|
||||
void forward_interrupts(int pid)
|
||||
{
|
||||
struct irq_fd *irq;
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
spin_lock_irqsave(&irq_lock, flags);
|
||||
for (irq = active_fds; irq != NULL; irq = irq->next) {
|
||||
err = os_set_owner(irq->fd, pid);
|
||||
if (err < 0) {
|
||||
/* XXX Just remove the irq rather than
|
||||
* print out an infinite stream of these
|
||||
*/
|
||||
printk("Failed to forward %d to pid %d, err = %d\n",
|
||||
irq->fd, pid, -err);
|
||||
}
|
||||
|
||||
irq->pid = pid;
|
||||
}
|
||||
spin_unlock_irqrestore(&irq_lock, flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* do_IRQ handles all normal device IRQ's (the special
|
||||
* SMP cross-CPU interrupts have their own specific
|
||||
* handlers).
|
||||
*/
|
||||
unsigned int do_IRQ(int irq, union uml_pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *old_regs = set_irq_regs((struct pt_regs *)regs);
|
||||
irq_enter();
|
||||
__do_IRQ(irq);
|
||||
irq_exit();
|
||||
set_irq_regs(old_regs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int um_request_irq(unsigned int irq, int fd, int type,
|
||||
irq_handler_t handler,
|
||||
unsigned long irqflags, const char * devname,
|
||||
void *dev_id)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = request_irq(irq, handler, irqflags, devname, dev_id);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (fd != -1)
|
||||
err = activate_fd(irq, fd, type, dev_id);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(um_request_irq);
|
||||
EXPORT_SYMBOL(reactivate_fd);
|
||||
|
||||
/* hw_interrupt_type must define (startup || enable) &&
|
||||
* (shutdown || disable) && end */
|
||||
static void dummy(unsigned int irq)
|
||||
{
|
||||
}
|
||||
|
||||
/* This is used for everything else than the timer. */
|
||||
static struct hw_interrupt_type normal_irq_type = {
|
||||
.typename = "SIGIO",
|
||||
.release = free_irq_by_irq_and_dev,
|
||||
.disable = dummy,
|
||||
.enable = dummy,
|
||||
.ack = dummy,
|
||||
.end = dummy
|
||||
};
|
||||
|
||||
static struct hw_interrupt_type SIGVTALRM_irq_type = {
|
||||
.typename = "SIGVTALRM",
|
||||
.release = free_irq_by_irq_and_dev,
|
||||
.shutdown = dummy, /* never called */
|
||||
.disable = dummy,
|
||||
.enable = dummy,
|
||||
.ack = dummy,
|
||||
.end = dummy
|
||||
};
|
||||
|
||||
void __init init_IRQ(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
irq_desc[TIMER_IRQ].status = IRQ_DISABLED;
|
||||
irq_desc[TIMER_IRQ].action = NULL;
|
||||
irq_desc[TIMER_IRQ].depth = 1;
|
||||
irq_desc[TIMER_IRQ].chip = &SIGVTALRM_irq_type;
|
||||
enable_irq(TIMER_IRQ);
|
||||
for (i = 1; i < NR_IRQS; i++) {
|
||||
irq_desc[i].status = IRQ_DISABLED;
|
||||
irq_desc[i].action = NULL;
|
||||
irq_desc[i].depth = 1;
|
||||
irq_desc[i].chip = &normal_irq_type;
|
||||
enable_irq(i);
|
||||
}
|
||||
}
|
||||
|
||||
int init_aio_irq(int irq, char *name, irq_handler_t handler)
|
||||
{
|
||||
int fds[2], err;
|
||||
|
||||
err = os_pipe(fds, 1, 1);
|
||||
if (err) {
|
||||
printk("init_aio_irq - os_pipe failed, err = %d\n", -err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = um_request_irq(irq, fds[0], IRQ_READ, handler,
|
||||
IRQF_DISABLED | IRQF_SAMPLE_RANDOM, name,
|
||||
(void *) (long) fds[0]);
|
||||
if (err) {
|
||||
printk("init_aio_irq - : um_request_irq failed, err = %d\n",
|
||||
err);
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
err = fds[1];
|
||||
goto out;
|
||||
|
||||
out_close:
|
||||
os_close_file(fds[0]);
|
||||
os_close_file(fds[1]);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
101
arch/um/kernel/ksyms.c
Normal file
101
arch/um/kernel/ksyms.c
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2001 - 2004 Jeff Dike (jdike@addtoit.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/module.h"
|
||||
#include "linux/string.h"
|
||||
#include "linux/smp_lock.h"
|
||||
#include "linux/spinlock.h"
|
||||
#include "linux/highmem.h"
|
||||
#include "asm/current.h"
|
||||
#include "asm/processor.h"
|
||||
#include "asm/unistd.h"
|
||||
#include "asm/pgalloc.h"
|
||||
#include "asm/pgtable.h"
|
||||
#include "asm/page.h"
|
||||
#include "asm/tlbflush.h"
|
||||
#include "kern_util.h"
|
||||
#include "user_util.h"
|
||||
#include "mem_user.h"
|
||||
#include "os.h"
|
||||
|
||||
EXPORT_SYMBOL(uml_physmem);
|
||||
EXPORT_SYMBOL(set_signals);
|
||||
EXPORT_SYMBOL(get_signals);
|
||||
EXPORT_SYMBOL(kernel_thread);
|
||||
EXPORT_SYMBOL(sys_waitpid);
|
||||
EXPORT_SYMBOL(task_size);
|
||||
EXPORT_SYMBOL(flush_tlb_range);
|
||||
EXPORT_SYMBOL(host_task_size);
|
||||
EXPORT_SYMBOL(arch_validate);
|
||||
EXPORT_SYMBOL(get_kmem_end);
|
||||
|
||||
EXPORT_SYMBOL(high_physmem);
|
||||
EXPORT_SYMBOL(empty_zero_page);
|
||||
EXPORT_SYMBOL(um_virt_to_phys);
|
||||
EXPORT_SYMBOL(mode_tt);
|
||||
EXPORT_SYMBOL(handle_page_fault);
|
||||
EXPORT_SYMBOL(find_iomem);
|
||||
|
||||
#ifdef CONFIG_MODE_TT
|
||||
EXPORT_SYMBOL(stop);
|
||||
EXPORT_SYMBOL(strncpy_from_user_tt);
|
||||
EXPORT_SYMBOL(copy_from_user_tt);
|
||||
EXPORT_SYMBOL(copy_to_user_tt);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MODE_SKAS
|
||||
EXPORT_SYMBOL(strnlen_user_skas);
|
||||
EXPORT_SYMBOL(strncpy_from_user_skas);
|
||||
EXPORT_SYMBOL(copy_to_user_skas);
|
||||
EXPORT_SYMBOL(copy_from_user_skas);
|
||||
EXPORT_SYMBOL(clear_user_skas);
|
||||
#endif
|
||||
EXPORT_SYMBOL(uml_strdup);
|
||||
|
||||
EXPORT_SYMBOL(os_stat_fd);
|
||||
EXPORT_SYMBOL(os_stat_file);
|
||||
EXPORT_SYMBOL(os_access);
|
||||
EXPORT_SYMBOL(os_print_error);
|
||||
EXPORT_SYMBOL(os_get_exec_close);
|
||||
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);
|
||||
EXPORT_SYMBOL(run_helper);
|
||||
EXPORT_SYMBOL(start_thread);
|
||||
EXPORT_SYMBOL(dump_thread);
|
||||
|
||||
EXPORT_SYMBOL(do_gettimeofday);
|
||||
EXPORT_SYMBOL(do_settimeofday);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
|
||||
/* required for SMP */
|
||||
|
||||
extern void FASTCALL( __write_lock_failed(rwlock_t *rw));
|
||||
EXPORT_SYMBOL(__write_lock_failed);
|
||||
|
||||
extern void FASTCALL( __read_lock_failed(rwlock_t *rw));
|
||||
EXPORT_SYMBOL(__read_lock_failed);
|
||||
|
||||
#endif
|
||||
370
arch/um/kernel/mem.c
Normal file
370
arch/um/kernel/mem.c
Normal file
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
* Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/stddef.h"
|
||||
#include "linux/kernel.h"
|
||||
#include "linux/mm.h"
|
||||
#include "linux/bootmem.h"
|
||||
#include "linux/swap.h"
|
||||
#include "linux/highmem.h"
|
||||
#include "linux/gfp.h"
|
||||
#include "asm/page.h"
|
||||
#include "asm/fixmap.h"
|
||||
#include "asm/pgalloc.h"
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "kern.h"
|
||||
#include "mem_user.h"
|
||||
#include "uml_uaccess.h"
|
||||
#include "os.h"
|
||||
#include "linux/types.h"
|
||||
#include "linux/string.h"
|
||||
#include "init.h"
|
||||
#include "kern_constants.h"
|
||||
|
||||
/* allocated in paging_init, zeroed in mem_init, and unchanged thereafter */
|
||||
unsigned long *empty_zero_page = NULL;
|
||||
/* allocated in paging_init and unchanged thereafter */
|
||||
unsigned long *empty_bad_page = NULL;
|
||||
pgd_t swapper_pg_dir[PTRS_PER_PGD];
|
||||
unsigned long long highmem;
|
||||
int kmalloc_ok = 0;
|
||||
|
||||
static unsigned long brk_end;
|
||||
|
||||
void unmap_physmem(void)
|
||||
{
|
||||
os_unmap_memory((void *) brk_end, uml_reserved - brk_end);
|
||||
}
|
||||
|
||||
static void map_cb(void *unused)
|
||||
{
|
||||
map_memory(brk_end, __pa(brk_end), uml_reserved - brk_end, 1, 1, 0);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HIGHMEM
|
||||
static void setup_highmem(unsigned long highmem_start,
|
||||
unsigned long highmem_len)
|
||||
{
|
||||
struct page *page;
|
||||
unsigned long highmem_pfn;
|
||||
int i;
|
||||
|
||||
highmem_pfn = __pa(highmem_start) >> PAGE_SHIFT;
|
||||
for(i = 0; i < highmem_len >> PAGE_SHIFT; i++){
|
||||
page = &mem_map[highmem_pfn + i];
|
||||
ClearPageReserved(page);
|
||||
init_page_count(page);
|
||||
__free_page(page);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void mem_init(void)
|
||||
{
|
||||
/* clear the zero-page */
|
||||
memset((void *) empty_zero_page, 0, PAGE_SIZE);
|
||||
|
||||
/* Map in the area just after the brk now that kmalloc is about
|
||||
* to be turned on.
|
||||
*/
|
||||
brk_end = (unsigned long) UML_ROUND_UP(sbrk(0));
|
||||
map_cb(NULL);
|
||||
initial_thread_cb(map_cb, NULL);
|
||||
free_bootmem(__pa(brk_end), uml_reserved - brk_end);
|
||||
uml_reserved = brk_end;
|
||||
|
||||
/* this will put all low memory onto the freelists */
|
||||
totalram_pages = free_all_bootmem();
|
||||
max_low_pfn = totalram_pages;
|
||||
#ifdef CONFIG_HIGHMEM
|
||||
totalhigh_pages = highmem >> PAGE_SHIFT;
|
||||
totalram_pages += totalhigh_pages;
|
||||
#endif
|
||||
num_physpages = totalram_pages;
|
||||
max_pfn = totalram_pages;
|
||||
printk(KERN_INFO "Memory: %luk available\n",
|
||||
(unsigned long) nr_free_pages() << (PAGE_SHIFT-10));
|
||||
kmalloc_ok = 1;
|
||||
|
||||
#ifdef CONFIG_HIGHMEM
|
||||
setup_highmem(end_iomem, highmem);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a page table and place a pointer to it in a middle page
|
||||
* directory entry.
|
||||
*/
|
||||
static void __init one_page_table_init(pmd_t *pmd)
|
||||
{
|
||||
if (pmd_none(*pmd)) {
|
||||
pte_t *pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);
|
||||
set_pmd(pmd, __pmd(_KERNPG_TABLE +
|
||||
(unsigned long) __pa(pte)));
|
||||
if (pte != pte_offset_kernel(pmd, 0))
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
static void __init one_md_table_init(pud_t *pud)
|
||||
{
|
||||
#ifdef CONFIG_3_LEVEL_PGTABLES
|
||||
pmd_t *pmd_table = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE);
|
||||
set_pud(pud, __pud(_KERNPG_TABLE + (unsigned long) __pa(pmd_table)));
|
||||
if (pmd_table != pmd_offset(pud, 0))
|
||||
BUG();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void __init fixrange_init(unsigned long start, unsigned long end,
|
||||
pgd_t *pgd_base)
|
||||
{
|
||||
pgd_t *pgd;
|
||||
pud_t *pud;
|
||||
pmd_t *pmd;
|
||||
int i, j;
|
||||
unsigned long vaddr;
|
||||
|
||||
vaddr = start;
|
||||
i = pgd_index(vaddr);
|
||||
j = pmd_index(vaddr);
|
||||
pgd = pgd_base + i;
|
||||
|
||||
for ( ; (i < PTRS_PER_PGD) && (vaddr < end); pgd++, i++) {
|
||||
pud = pud_offset(pgd, vaddr);
|
||||
if (pud_none(*pud))
|
||||
one_md_table_init(pud);
|
||||
pmd = pmd_offset(pud, vaddr);
|
||||
for (; (j < PTRS_PER_PMD) && (vaddr != end); pmd++, j++) {
|
||||
one_page_table_init(pmd);
|
||||
vaddr += PMD_SIZE;
|
||||
}
|
||||
j = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HIGHMEM
|
||||
pte_t *kmap_pte;
|
||||
pgprot_t kmap_prot;
|
||||
|
||||
#define kmap_get_fixmap_pte(vaddr) \
|
||||
pte_offset_kernel(pmd_offset(pud_offset(pgd_offset_k(vaddr), (vaddr)),\
|
||||
(vaddr)), (vaddr))
|
||||
|
||||
static void __init kmap_init(void)
|
||||
{
|
||||
unsigned long kmap_vstart;
|
||||
|
||||
/* cache the first kmap pte */
|
||||
kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN);
|
||||
kmap_pte = kmap_get_fixmap_pte(kmap_vstart);
|
||||
|
||||
kmap_prot = PAGE_KERNEL;
|
||||
}
|
||||
|
||||
static void init_highmem(void)
|
||||
{
|
||||
pgd_t *pgd;
|
||||
pud_t *pud;
|
||||
pmd_t *pmd;
|
||||
pte_t *pte;
|
||||
unsigned long vaddr;
|
||||
|
||||
/*
|
||||
* Permanent kmaps:
|
||||
*/
|
||||
vaddr = PKMAP_BASE;
|
||||
fixrange_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, swapper_pg_dir);
|
||||
|
||||
pgd = swapper_pg_dir + pgd_index(vaddr);
|
||||
pud = pud_offset(pgd, vaddr);
|
||||
pmd = pmd_offset(pud, vaddr);
|
||||
pte = pte_offset_kernel(pmd, vaddr);
|
||||
pkmap_page_table = pte;
|
||||
|
||||
kmap_init();
|
||||
}
|
||||
#endif /* CONFIG_HIGHMEM */
|
||||
|
||||
static void __init fixaddr_user_init( void)
|
||||
{
|
||||
#ifdef CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA
|
||||
long size = FIXADDR_USER_END - FIXADDR_USER_START;
|
||||
pgd_t *pgd;
|
||||
pud_t *pud;
|
||||
pmd_t *pmd;
|
||||
pte_t *pte;
|
||||
unsigned long paddr, vaddr = FIXADDR_USER_START;
|
||||
|
||||
if ( ! size )
|
||||
return;
|
||||
|
||||
fixrange_init( FIXADDR_USER_START, FIXADDR_USER_END, swapper_pg_dir);
|
||||
paddr = (unsigned long)alloc_bootmem_low_pages( size);
|
||||
memcpy( (void *)paddr, (void *)FIXADDR_USER_START, size);
|
||||
paddr = __pa(paddr);
|
||||
for ( ; size > 0; size-=PAGE_SIZE, vaddr+=PAGE_SIZE, paddr+=PAGE_SIZE){
|
||||
pgd = swapper_pg_dir + pgd_index(vaddr);
|
||||
pud = pud_offset(pgd, vaddr);
|
||||
pmd = pmd_offset(pud, vaddr);
|
||||
pte = pte_offset_kernel(pmd, vaddr);
|
||||
pte_set_val( (*pte), paddr, PAGE_READONLY);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void paging_init(void)
|
||||
{
|
||||
unsigned long zones_size[MAX_NR_ZONES], vaddr;
|
||||
int i;
|
||||
|
||||
empty_zero_page = (unsigned long *) alloc_bootmem_low_pages(PAGE_SIZE);
|
||||
empty_bad_page = (unsigned long *) alloc_bootmem_low_pages(PAGE_SIZE);
|
||||
for(i = 0; i < ARRAY_SIZE(zones_size); i++)
|
||||
zones_size[i] = 0;
|
||||
|
||||
zones_size[ZONE_NORMAL] = (end_iomem >> PAGE_SHIFT) -
|
||||
(uml_physmem >> PAGE_SHIFT);
|
||||
#ifdef CONFIG_HIGHMEM
|
||||
zones_size[ZONE_HIGHMEM] = highmem >> PAGE_SHIFT;
|
||||
#endif
|
||||
free_area_init(zones_size);
|
||||
|
||||
/*
|
||||
* Fixed mappings, only the page table structure has to be
|
||||
* created - mappings will be set by set_fixmap():
|
||||
*/
|
||||
vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;
|
||||
fixrange_init(vaddr, FIXADDR_TOP, swapper_pg_dir);
|
||||
|
||||
fixaddr_user_init();
|
||||
|
||||
#ifdef CONFIG_HIGHMEM
|
||||
init_highmem();
|
||||
#endif
|
||||
}
|
||||
|
||||
struct page *arch_validate(struct page *page, gfp_t mask, int order)
|
||||
{
|
||||
unsigned long addr, zero = 0;
|
||||
int i;
|
||||
|
||||
again:
|
||||
if(page == NULL)
|
||||
return page;
|
||||
if(PageHighMem(page))
|
||||
return page;
|
||||
|
||||
addr = (unsigned long) page_address(page);
|
||||
for(i = 0; i < (1 << order); i++){
|
||||
current->thread.fault_addr = (void *) addr;
|
||||
if(__do_copy_to_user((void __user *) addr, &zero,
|
||||
sizeof(zero),
|
||||
¤t->thread.fault_addr,
|
||||
¤t->thread.fault_catcher)){
|
||||
if(!(mask & __GFP_WAIT))
|
||||
return NULL;
|
||||
else break;
|
||||
}
|
||||
addr += PAGE_SIZE;
|
||||
}
|
||||
|
||||
if(i == (1 << order))
|
||||
return page;
|
||||
page = alloc_pages(mask, order);
|
||||
goto again;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_INITRD
|
||||
void free_initrd_mem(unsigned long start, unsigned long end)
|
||||
{
|
||||
if (start < end)
|
||||
printk ("Freeing initrd memory: %ldk freed\n",
|
||||
(end - start) >> 10);
|
||||
for (; start < end; start += PAGE_SIZE) {
|
||||
ClearPageReserved(virt_to_page(start));
|
||||
init_page_count(virt_to_page(start));
|
||||
free_page(start);
|
||||
totalram_pages++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void show_mem(void)
|
||||
{
|
||||
int pfn, total = 0, reserved = 0;
|
||||
int shared = 0, cached = 0;
|
||||
int highmem = 0;
|
||||
struct page *page;
|
||||
|
||||
printk("Mem-info:\n");
|
||||
show_free_areas();
|
||||
printk("Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10));
|
||||
pfn = max_mapnr;
|
||||
while(pfn-- > 0) {
|
||||
page = pfn_to_page(pfn);
|
||||
total++;
|
||||
if(PageHighMem(page))
|
||||
highmem++;
|
||||
if(PageReserved(page))
|
||||
reserved++;
|
||||
else if(PageSwapCache(page))
|
||||
cached++;
|
||||
else if(page_count(page))
|
||||
shared += page_count(page) - 1;
|
||||
}
|
||||
printk("%d pages of RAM\n", total);
|
||||
printk("%d pages of HIGHMEM\n", highmem);
|
||||
printk("%d reserved pages\n", reserved);
|
||||
printk("%d pages shared\n", shared);
|
||||
printk("%d pages swap cached\n", cached);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate and free page tables.
|
||||
*/
|
||||
|
||||
pgd_t *pgd_alloc(struct mm_struct *mm)
|
||||
{
|
||||
pgd_t *pgd = (pgd_t *)__get_free_page(GFP_KERNEL);
|
||||
|
||||
if (pgd) {
|
||||
memset(pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
|
||||
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 pgd_free(pgd_t *pgd)
|
||||
{
|
||||
free_page((unsigned long) pgd);
|
||||
}
|
||||
|
||||
pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address)
|
||||
{
|
||||
pte_t *pte;
|
||||
|
||||
pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO);
|
||||
return pte;
|
||||
}
|
||||
|
||||
struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address)
|
||||
{
|
||||
struct page *pte;
|
||||
|
||||
pte = alloc_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO);
|
||||
return pte;
|
||||
}
|
||||
458
arch/um/kernel/physmem.c
Normal file
458
arch/um/kernel/physmem.c
Normal file
@@ -0,0 +1,458 @@
|
||||
/*
|
||||
* Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/mm.h"
|
||||
#include "linux/rbtree.h"
|
||||
#include "linux/slab.h"
|
||||
#include "linux/vmalloc.h"
|
||||
#include "linux/bootmem.h"
|
||||
#include "linux/module.h"
|
||||
#include "linux/pfn.h"
|
||||
#include "asm/types.h"
|
||||
#include "asm/pgtable.h"
|
||||
#include "kern_util.h"
|
||||
#include "user_util.h"
|
||||
#include "mode_kern.h"
|
||||
#include "mem.h"
|
||||
#include "mem_user.h"
|
||||
#include "os.h"
|
||||
#include "kern.h"
|
||||
#include "init.h"
|
||||
|
||||
struct phys_desc {
|
||||
struct rb_node rb;
|
||||
int fd;
|
||||
__u64 offset;
|
||||
void *virt;
|
||||
unsigned long phys;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static struct rb_root phys_mappings = RB_ROOT;
|
||||
|
||||
static struct rb_node **find_rb(void *virt)
|
||||
{
|
||||
struct rb_node **n = &phys_mappings.rb_node;
|
||||
struct phys_desc *d;
|
||||
|
||||
while(*n != NULL){
|
||||
d = rb_entry(*n, struct phys_desc, rb);
|
||||
if(d->virt == virt)
|
||||
return n;
|
||||
|
||||
if(d->virt > virt)
|
||||
n = &(*n)->rb_left;
|
||||
else
|
||||
n = &(*n)->rb_right;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static struct phys_desc *find_phys_mapping(void *virt)
|
||||
{
|
||||
struct rb_node **n = find_rb(virt);
|
||||
|
||||
if(*n == NULL)
|
||||
return NULL;
|
||||
|
||||
return rb_entry(*n, struct phys_desc, rb);
|
||||
}
|
||||
|
||||
static void insert_phys_mapping(struct phys_desc *desc)
|
||||
{
|
||||
struct rb_node **n = find_rb(desc->virt);
|
||||
|
||||
if(*n != NULL)
|
||||
panic("Physical remapping for %p already present",
|
||||
desc->virt);
|
||||
|
||||
rb_link_node(&desc->rb, rb_parent(*n), n);
|
||||
rb_insert_color(&desc->rb, &phys_mappings);
|
||||
}
|
||||
|
||||
LIST_HEAD(descriptor_mappings);
|
||||
|
||||
struct desc_mapping {
|
||||
int fd;
|
||||
struct list_head list;
|
||||
struct list_head pages;
|
||||
};
|
||||
|
||||
static struct desc_mapping *find_mapping(int fd)
|
||||
{
|
||||
struct desc_mapping *desc;
|
||||
struct list_head *ele;
|
||||
|
||||
list_for_each(ele, &descriptor_mappings){
|
||||
desc = list_entry(ele, struct desc_mapping, list);
|
||||
if(desc->fd == fd)
|
||||
return desc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct desc_mapping *descriptor_mapping(int fd)
|
||||
{
|
||||
struct desc_mapping *desc;
|
||||
|
||||
desc = find_mapping(fd);
|
||||
if(desc != NULL)
|
||||
return desc;
|
||||
|
||||
desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
|
||||
if(desc == NULL)
|
||||
return NULL;
|
||||
|
||||
*desc = ((struct desc_mapping)
|
||||
{ .fd = fd,
|
||||
.list = LIST_HEAD_INIT(desc->list),
|
||||
.pages = LIST_HEAD_INIT(desc->pages) });
|
||||
list_add(&desc->list, &descriptor_mappings);
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
int physmem_subst_mapping(void *virt, int fd, __u64 offset, int w)
|
||||
{
|
||||
struct desc_mapping *fd_maps;
|
||||
struct phys_desc *desc;
|
||||
unsigned long phys;
|
||||
int err;
|
||||
|
||||
fd_maps = descriptor_mapping(fd);
|
||||
if(fd_maps == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
phys = __pa(virt);
|
||||
desc = find_phys_mapping(virt);
|
||||
if(desc != NULL)
|
||||
panic("Address 0x%p is already substituted\n", virt);
|
||||
|
||||
err = -ENOMEM;
|
||||
desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
|
||||
if(desc == NULL)
|
||||
goto out;
|
||||
|
||||
*desc = ((struct phys_desc)
|
||||
{ .fd = fd,
|
||||
.offset = offset,
|
||||
.virt = virt,
|
||||
.phys = __pa(virt),
|
||||
.list = LIST_HEAD_INIT(desc->list) });
|
||||
insert_phys_mapping(desc);
|
||||
|
||||
list_add(&desc->list, &fd_maps->pages);
|
||||
|
||||
virt = (void *) ((unsigned long) virt & PAGE_MASK);
|
||||
err = os_map_memory(virt, fd, offset, PAGE_SIZE, 1, w, 0);
|
||||
if(!err)
|
||||
goto out;
|
||||
|
||||
rb_erase(&desc->rb, &phys_mappings);
|
||||
kfree(desc);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int physmem_fd = -1;
|
||||
|
||||
static void remove_mapping(struct phys_desc *desc)
|
||||
{
|
||||
void *virt = desc->virt;
|
||||
int err;
|
||||
|
||||
rb_erase(&desc->rb, &phys_mappings);
|
||||
list_del(&desc->list);
|
||||
kfree(desc);
|
||||
|
||||
err = os_map_memory(virt, physmem_fd, __pa(virt), PAGE_SIZE, 1, 1, 0);
|
||||
if(err)
|
||||
panic("Failed to unmap block device page from physical memory, "
|
||||
"errno = %d", -err);
|
||||
}
|
||||
|
||||
int physmem_remove_mapping(void *virt)
|
||||
{
|
||||
struct phys_desc *desc;
|
||||
|
||||
virt = (void *) ((unsigned long) virt & PAGE_MASK);
|
||||
desc = find_phys_mapping(virt);
|
||||
if(desc == NULL)
|
||||
return 0;
|
||||
|
||||
remove_mapping(desc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void physmem_forget_descriptor(int fd)
|
||||
{
|
||||
struct desc_mapping *desc;
|
||||
struct phys_desc *page;
|
||||
struct list_head *ele, *next;
|
||||
__u64 offset;
|
||||
void *addr;
|
||||
int err;
|
||||
|
||||
desc = find_mapping(fd);
|
||||
if(desc == NULL)
|
||||
return;
|
||||
|
||||
list_for_each_safe(ele, next, &desc->pages){
|
||||
page = list_entry(ele, struct phys_desc, list);
|
||||
offset = page->offset;
|
||||
addr = page->virt;
|
||||
remove_mapping(page);
|
||||
err = os_seek_file(fd, offset);
|
||||
if(err)
|
||||
panic("physmem_forget_descriptor - failed to seek "
|
||||
"to %lld in fd %d, error = %d\n",
|
||||
offset, fd, -err);
|
||||
err = os_read_file(fd, addr, PAGE_SIZE);
|
||||
if(err < 0)
|
||||
panic("physmem_forget_descriptor - failed to read "
|
||||
"from fd %d to 0x%p, error = %d\n",
|
||||
fd, addr, -err);
|
||||
}
|
||||
|
||||
list_del(&desc->list);
|
||||
kfree(desc);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(physmem_forget_descriptor);
|
||||
EXPORT_SYMBOL(physmem_remove_mapping);
|
||||
EXPORT_SYMBOL(physmem_subst_mapping);
|
||||
|
||||
void arch_free_page(struct page *page, int order)
|
||||
{
|
||||
void *virt;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < (1 << order); i++){
|
||||
virt = __va(page_to_phys(page + i));
|
||||
physmem_remove_mapping(virt);
|
||||
}
|
||||
}
|
||||
|
||||
int is_remapped(void *virt)
|
||||
{
|
||||
struct phys_desc *desc = find_phys_mapping(virt);
|
||||
|
||||
return desc != NULL;
|
||||
}
|
||||
|
||||
/* Changed during early boot */
|
||||
unsigned long high_physmem;
|
||||
|
||||
extern unsigned long long physmem_size;
|
||||
|
||||
int init_maps(unsigned long physmem, unsigned long iomem, unsigned long highmem)
|
||||
{
|
||||
struct page *p, *map;
|
||||
unsigned long phys_len, phys_pages, highmem_len, highmem_pages;
|
||||
unsigned long iomem_len, iomem_pages, total_len, total_pages;
|
||||
int i;
|
||||
|
||||
phys_pages = physmem >> PAGE_SHIFT;
|
||||
phys_len = phys_pages * sizeof(struct page);
|
||||
|
||||
iomem_pages = iomem >> PAGE_SHIFT;
|
||||
iomem_len = iomem_pages * sizeof(struct page);
|
||||
|
||||
highmem_pages = highmem >> PAGE_SHIFT;
|
||||
highmem_len = highmem_pages * sizeof(struct page);
|
||||
|
||||
total_pages = phys_pages + iomem_pages + highmem_pages;
|
||||
total_len = phys_len + iomem_len + highmem_len;
|
||||
|
||||
if(kmalloc_ok){
|
||||
map = kmalloc(total_len, GFP_KERNEL);
|
||||
if(map == NULL)
|
||||
map = vmalloc(total_len);
|
||||
}
|
||||
else map = alloc_bootmem_low_pages(total_len);
|
||||
|
||||
if(map == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
for(i = 0; i < total_pages; i++){
|
||||
p = &map[i];
|
||||
memset(p, 0, sizeof(struct page));
|
||||
SetPageReserved(p);
|
||||
INIT_LIST_HEAD(&p->lru);
|
||||
}
|
||||
|
||||
max_mapnr = total_pages;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Changed during early boot */
|
||||
static unsigned long kmem_top = 0;
|
||||
|
||||
unsigned long get_kmem_end(void)
|
||||
{
|
||||
if(kmem_top == 0)
|
||||
kmem_top = CHOOSE_MODE(kmem_end_tt, kmem_end_skas);
|
||||
return kmem_top;
|
||||
}
|
||||
|
||||
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("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);
|
||||
}
|
||||
}
|
||||
|
||||
extern int __syscall_stub_start;
|
||||
|
||||
void setup_physmem(unsigned long start, unsigned long reserve_end,
|
||||
unsigned long len, unsigned long long highmem)
|
||||
{
|
||||
unsigned long reserve = reserve_end - start;
|
||||
int pfn = PFN_UP(__pa(reserve_end));
|
||||
int delta = (len - reserve) >> PAGE_SHIFT;
|
||||
int err, offset, bootmap_size;
|
||||
|
||||
physmem_fd = create_mem_file(len + highmem);
|
||||
|
||||
offset = uml_reserved - uml_physmem;
|
||||
err = os_map_memory((void *) uml_reserved, physmem_fd, offset,
|
||||
len - offset, 1, 1, 0);
|
||||
if(err < 0){
|
||||
os_print_error(err, "Mapping memory");
|
||||
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);
|
||||
|
||||
bootmap_size = init_bootmem(pfn, pfn + delta);
|
||||
free_bootmem(__pa(reserve_end) + bootmap_size,
|
||||
len - bootmap_size - reserve);
|
||||
}
|
||||
|
||||
int phys_mapping(unsigned long phys, __u64 *offset_out)
|
||||
{
|
||||
struct phys_desc *desc = find_phys_mapping(__va(phys & PAGE_MASK));
|
||||
int fd = -1;
|
||||
|
||||
if(desc != NULL){
|
||||
fd = desc->fd;
|
||||
*offset_out = desc->offset;
|
||||
}
|
||||
else if(phys < physmem_size){
|
||||
fd = physmem_fd;
|
||||
*offset_out = phys;
|
||||
}
|
||||
else if(phys < __pa(end_iomem)){
|
||||
struct iomem_region *region = iomem_regions;
|
||||
|
||||
while(region != NULL){
|
||||
if((phys >= region->phys) &&
|
||||
(phys < region->phys + region->size)){
|
||||
fd = region->fd;
|
||||
*offset_out = phys - region->phys;
|
||||
break;
|
||||
}
|
||||
region = region->next;
|
||||
}
|
||||
}
|
||||
else if(phys < __pa(end_iomem) + highmem){
|
||||
fd = physmem_fd;
|
||||
*offset_out = phys - iomem_size;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int __init uml_mem_setup(char *line, int *add)
|
||||
{
|
||||
char *retptr;
|
||||
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"
|
||||
);
|
||||
|
||||
extern int __init parse_iomem(char *str, int *add);
|
||||
|
||||
__uml_setup("iomem=", parse_iomem,
|
||||
"iomem=<name>,<file>\n"
|
||||
" Configure <file> as an IO memory region named <name>.\n\n"
|
||||
);
|
||||
|
||||
/*
|
||||
* This list is constructed in parse_iomem and addresses filled in in
|
||||
* setup_iomem, both of which run during early boot. Afterwards, it's
|
||||
* unchanged.
|
||||
*/
|
||||
struct iomem_region *iomem_regions = NULL;
|
||||
|
||||
/* Initialized in parse_iomem */
|
||||
int iomem_size = 0;
|
||||
|
||||
unsigned long find_iomem(char *driver, unsigned long *len_out)
|
||||
{
|
||||
struct iomem_region *region = iomem_regions;
|
||||
|
||||
while(region != NULL){
|
||||
if(!strcmp(region->driver, driver)){
|
||||
*len_out = region->size;
|
||||
return region->virt;
|
||||
}
|
||||
|
||||
region = region->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int setup_iomem(void)
|
||||
{
|
||||
struct iomem_region *region = iomem_regions;
|
||||
unsigned long iomem_start = high_physmem + PAGE_SIZE;
|
||||
int err;
|
||||
|
||||
while(region != NULL){
|
||||
err = os_map_memory((void *) iomem_start, region->fd, 0,
|
||||
region->size, 1, 1, 0);
|
||||
if(err)
|
||||
printk("Mapping iomem region for driver '%s' failed, "
|
||||
"errno = %d\n", region->driver, -err);
|
||||
else {
|
||||
region->virt = iomem_start;
|
||||
region->phys = __pa(region->virt);
|
||||
}
|
||||
|
||||
iomem_start += region->size + PAGE_SIZE;
|
||||
region = region->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__initcall(setup_iomem);
|
||||
484
arch/um/kernel/process.c
Normal file
484
arch/um/kernel/process.c
Normal file
@@ -0,0 +1,484 @@
|
||||
/*
|
||||
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Copyright 2003 PathScale, Inc.
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/kernel.h"
|
||||
#include "linux/sched.h"
|
||||
#include "linux/interrupt.h"
|
||||
#include "linux/string.h"
|
||||
#include "linux/mm.h"
|
||||
#include "linux/slab.h"
|
||||
#include "linux/utsname.h"
|
||||
#include "linux/fs.h"
|
||||
#include "linux/utime.h"
|
||||
#include "linux/smp_lock.h"
|
||||
#include "linux/module.h"
|
||||
#include "linux/init.h"
|
||||
#include "linux/capability.h"
|
||||
#include "linux/vmalloc.h"
|
||||
#include "linux/spinlock.h"
|
||||
#include "linux/proc_fs.h"
|
||||
#include "linux/ptrace.h"
|
||||
#include "linux/random.h"
|
||||
#include "linux/personality.h"
|
||||
#include "asm/unistd.h"
|
||||
#include "asm/mman.h"
|
||||
#include "asm/segment.h"
|
||||
#include "asm/stat.h"
|
||||
#include "asm/pgtable.h"
|
||||
#include "asm/processor.h"
|
||||
#include "asm/tlbflush.h"
|
||||
#include "asm/uaccess.h"
|
||||
#include "asm/user.h"
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "kern.h"
|
||||
#include "signal_kern.h"
|
||||
#include "init.h"
|
||||
#include "irq_user.h"
|
||||
#include "mem_user.h"
|
||||
#include "tlb.h"
|
||||
#include "frame_kern.h"
|
||||
#include "sigcontext.h"
|
||||
#include "os.h"
|
||||
#include "mode.h"
|
||||
#include "mode_kern.h"
|
||||
#include "choose-mode.h"
|
||||
#include "um_malloc.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 cpu_task cpu_tasks[NR_CPUS] = { [0 ... NR_CPUS - 1] = { -1, NULL } };
|
||||
|
||||
int external_pid(void *t)
|
||||
{
|
||||
struct task_struct *task = t ? t : current;
|
||||
|
||||
return(CHOOSE_MODE_PROC(external_pid_tt, external_pid_skas, task));
|
||||
}
|
||||
|
||||
int pid_to_processor_id(int pid)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < ncpus; i++){
|
||||
if(cpu_tasks[i].pid == pid) return(i);
|
||||
}
|
||||
return(-1);
|
||||
}
|
||||
|
||||
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);
|
||||
if(page == 0)
|
||||
return(0);
|
||||
stack_protections(page);
|
||||
return(page);
|
||||
}
|
||||
|
||||
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
|
||||
{
|
||||
int pid;
|
||||
|
||||
current->thread.request.u.thread.proc = fn;
|
||||
current->thread.request.u.thread.arg = arg;
|
||||
pid = do_fork(CLONE_VM | CLONE_UNTRACED | flags, 0,
|
||||
¤t->thread.regs, 0, NULL, NULL);
|
||||
if(pid < 0)
|
||||
panic("do_fork failed in kernel_thread, errno = %d", pid);
|
||||
return(pid);
|
||||
}
|
||||
|
||||
void set_current(void *t)
|
||||
{
|
||||
struct task_struct *task = t;
|
||||
|
||||
cpu_tasks[task_thread_info(task)->cpu] = ((struct cpu_task)
|
||||
{ external_pid(task), task });
|
||||
}
|
||||
|
||||
void *_switch_to(void *prev, void *next, void *last)
|
||||
{
|
||||
struct task_struct *from = prev;
|
||||
struct task_struct *to= next;
|
||||
|
||||
to->thread.prev_sched = from;
|
||||
set_current(to);
|
||||
|
||||
do {
|
||||
current->thread.saved_task = NULL ;
|
||||
CHOOSE_MODE_PROC(switch_to_tt, switch_to_skas, prev, next);
|
||||
if(current->thread.saved_task)
|
||||
show_regs(&(current->thread.regs));
|
||||
next= current->thread.saved_task;
|
||||
prev= current;
|
||||
} while(current->thread.saved_task);
|
||||
|
||||
return(current->thread.prev_sched);
|
||||
|
||||
}
|
||||
|
||||
void interrupt_end(void)
|
||||
{
|
||||
if(need_resched()) schedule();
|
||||
if(test_tsk_thread_flag(current, TIF_SIGPENDING)) do_signal();
|
||||
}
|
||||
|
||||
void release_thread(struct task_struct *task)
|
||||
{
|
||||
CHOOSE_MODE(release_thread_tt(task), release_thread_skas(task));
|
||||
}
|
||||
|
||||
void exit_thread(void)
|
||||
{
|
||||
unprotect_stack((unsigned long) current_thread);
|
||||
}
|
||||
|
||||
void *get_current(void)
|
||||
{
|
||||
return(current);
|
||||
}
|
||||
|
||||
int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
|
||||
unsigned long stack_top, struct task_struct * p,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
int ret;
|
||||
|
||||
p->thread = (struct thread_struct) INIT_THREAD;
|
||||
ret = CHOOSE_MODE_PROC(copy_thread_tt, copy_thread_skas, nr,
|
||||
clone_flags, sp, stack_top, p, regs);
|
||||
|
||||
if (ret || !current->thread.forking)
|
||||
goto out;
|
||||
|
||||
clear_flushed_tls(p);
|
||||
|
||||
/*
|
||||
* Set a new TLS for the child thread?
|
||||
*/
|
||||
if (clone_flags & CLONE_SETTLS)
|
||||
ret = arch_copy_tls(p);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void initial_thread_cb(void (*proc)(void *), void *arg)
|
||||
{
|
||||
int save_kmalloc_ok = kmalloc_ok;
|
||||
|
||||
kmalloc_ok = 0;
|
||||
CHOOSE_MODE_PROC(initial_thread_cb_tt, initial_thread_cb_skas, proc,
|
||||
arg);
|
||||
kmalloc_ok = save_kmalloc_ok;
|
||||
}
|
||||
|
||||
unsigned long stack_sp(unsigned long page)
|
||||
{
|
||||
return(page + PAGE_SIZE - sizeof(void *));
|
||||
}
|
||||
|
||||
int current_pid(void)
|
||||
{
|
||||
return(current->pid);
|
||||
}
|
||||
|
||||
void default_idle(void)
|
||||
{
|
||||
CHOOSE_MODE(uml_idle_timer(), (void) 0);
|
||||
|
||||
while(1){
|
||||
/* endless idle loop with no priority at all */
|
||||
|
||||
/*
|
||||
* although we are an idle CPU, we do not want to
|
||||
* get into the scheduler unnecessarily.
|
||||
*/
|
||||
if(need_resched())
|
||||
schedule();
|
||||
|
||||
idle_sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_idle(void)
|
||||
{
|
||||
CHOOSE_MODE(init_idle_tt(), init_idle_skas());
|
||||
}
|
||||
|
||||
int page_size(void)
|
||||
{
|
||||
return(PAGE_SIZE);
|
||||
}
|
||||
|
||||
void *um_virt_to_phys(struct task_struct *task, unsigned long addr,
|
||||
pte_t *pte_out)
|
||||
{
|
||||
pgd_t *pgd;
|
||||
pud_t *pud;
|
||||
pmd_t *pmd;
|
||||
pte_t *pte;
|
||||
pte_t ptent;
|
||||
|
||||
if(task->mm == NULL)
|
||||
return(ERR_PTR(-EINVAL));
|
||||
pgd = pgd_offset(task->mm, addr);
|
||||
if(!pgd_present(*pgd))
|
||||
return(ERR_PTR(-EINVAL));
|
||||
|
||||
pud = pud_offset(pgd, addr);
|
||||
if(!pud_present(*pud))
|
||||
return(ERR_PTR(-EINVAL));
|
||||
|
||||
pmd = pmd_offset(pud, addr);
|
||||
if(!pmd_present(*pmd))
|
||||
return(ERR_PTR(-EINVAL));
|
||||
|
||||
pte = pte_offset_kernel(pmd, addr);
|
||||
ptent = *pte;
|
||||
if(!pte_present(ptent))
|
||||
return(ERR_PTR(-EINVAL));
|
||||
|
||||
if(pte_out != NULL)
|
||||
*pte_out = ptent;
|
||||
return((void *) (pte_val(ptent) & PAGE_MASK) + (addr & ~PAGE_MASK));
|
||||
}
|
||||
|
||||
char *current_cmd(void)
|
||||
{
|
||||
#if defined(CONFIG_SMP) || defined(CONFIG_HIGHMEM)
|
||||
return("(Unknown)");
|
||||
#else
|
||||
void *addr = um_virt_to_phys(current, current->mm->arg_start, NULL);
|
||||
return IS_ERR(addr) ? "(Unknown)": __va((unsigned long) addr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void force_sigbus(void)
|
||||
{
|
||||
printk(KERN_ERR "Killing pid %d because of a lack of memory\n",
|
||||
current->pid);
|
||||
lock_kernel();
|
||||
sigaddset(¤t->pending.signal, SIGBUS);
|
||||
recalc_sigpending();
|
||||
current->flags |= PF_SIGNALED;
|
||||
do_exit(SIGBUS | 0x80);
|
||||
}
|
||||
|
||||
void dump_thread(struct pt_regs *regs, struct user *u)
|
||||
{
|
||||
}
|
||||
|
||||
void enable_hlt(void)
|
||||
{
|
||||
panic("enable_hlt");
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(enable_hlt);
|
||||
|
||||
void disable_hlt(void)
|
||||
{
|
||||
panic("disable_hlt");
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(disable_hlt);
|
||||
|
||||
void *um_kmalloc(int size)
|
||||
{
|
||||
return kmalloc(size, GFP_KERNEL);
|
||||
}
|
||||
|
||||
void *um_kmalloc_atomic(int size)
|
||||
{
|
||||
return kmalloc(size, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
void *um_vmalloc(int size)
|
||||
{
|
||||
return vmalloc(size);
|
||||
}
|
||||
|
||||
void *um_vmalloc_atomic(int size)
|
||||
{
|
||||
return __vmalloc(size, GFP_ATOMIC | __GFP_HIGHMEM, PAGE_KERNEL);
|
||||
}
|
||||
|
||||
int __cant_sleep(void) {
|
||||
return in_atomic() || irqs_disabled() || in_interrupt();
|
||||
/* Is in_interrupt() really needed? */
|
||||
}
|
||||
|
||||
unsigned long get_fault_addr(void)
|
||||
{
|
||||
return((unsigned long) current->thread.fault_addr);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(get_fault_addr);
|
||||
|
||||
void not_implemented(void)
|
||||
{
|
||||
printk(KERN_DEBUG "Something isn't implemented in here\n");
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(not_implemented);
|
||||
|
||||
int user_context(unsigned long sp)
|
||||
{
|
||||
unsigned long stack;
|
||||
|
||||
stack = sp & (PAGE_MASK << CONFIG_KERNEL_STACK_ORDER);
|
||||
return(stack != (unsigned long) current_thread);
|
||||
}
|
||||
|
||||
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(char *string)
|
||||
{
|
||||
return kstrdup(string, GFP_KERNEL);
|
||||
}
|
||||
|
||||
int copy_to_user_proc(void __user *to, void *from, int size)
|
||||
{
|
||||
return(copy_to_user(to, from, size));
|
||||
}
|
||||
|
||||
int copy_from_user_proc(void *to, void __user *from, int size)
|
||||
{
|
||||
return(copy_from_user(to, from, size));
|
||||
}
|
||||
|
||||
int clear_user_proc(void __user *buf, int size)
|
||||
{
|
||||
return(clear_user(buf, size));
|
||||
}
|
||||
|
||||
int strlen_user_proc(char __user *str)
|
||||
{
|
||||
return(strlen_user(str));
|
||||
}
|
||||
|
||||
int smp_sigio_handler(void)
|
||||
{
|
||||
#ifdef CONFIG_SMP
|
||||
int cpu = current_thread->cpu;
|
||||
IPI_handler(cpu);
|
||||
if(cpu != 0)
|
||||
return(1);
|
||||
#endif
|
||||
return(0);
|
||||
}
|
||||
|
||||
int cpu(void)
|
||||
{
|
||||
return(current_thread->cpu);
|
||||
}
|
||||
|
||||
static atomic_t using_sysemu = ATOMIC_INIT(0);
|
||||
int sysemu_supported;
|
||||
|
||||
void set_using_sysemu(int value)
|
||||
{
|
||||
if (value > sysemu_supported)
|
||||
return;
|
||||
atomic_set(&using_sysemu, value);
|
||||
}
|
||||
|
||||
int get_using_sysemu(void)
|
||||
{
|
||||
return atomic_read(&using_sysemu);
|
||||
}
|
||||
|
||||
static int proc_read_sysemu(char *buf, char **start, off_t offset, int size,int *eof, void *data)
|
||||
{
|
||||
if (snprintf(buf, size, "%d\n", get_using_sysemu()) < size) /*No overflow*/
|
||||
*eof = 1;
|
||||
|
||||
return strlen(buf);
|
||||
}
|
||||
|
||||
static int proc_write_sysemu(struct file *file,const char __user *buf, unsigned long count,void *data)
|
||||
{
|
||||
char tmp[2];
|
||||
|
||||
if (copy_from_user(tmp, buf, 1))
|
||||
return -EFAULT;
|
||||
|
||||
if (tmp[0] >= '0' && tmp[0] <= '2')
|
||||
set_using_sysemu(tmp[0] - '0');
|
||||
return count; /*We use the first char, but pretend to write everything*/
|
||||
}
|
||||
|
||||
int __init make_proc_sysemu(void)
|
||||
{
|
||||
struct proc_dir_entry *ent;
|
||||
if (!sysemu_supported)
|
||||
return 0;
|
||||
|
||||
ent = create_proc_entry("sysemu", 0600, &proc_root);
|
||||
|
||||
if (ent == NULL)
|
||||
{
|
||||
printk(KERN_WARNING "Failed to register /proc/sysemu\n");
|
||||
return(0);
|
||||
}
|
||||
|
||||
ent->read_proc = proc_read_sysemu;
|
||||
ent->write_proc = proc_write_sysemu;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
late_initcall(make_proc_sysemu);
|
||||
|
||||
int singlestepping(void * t)
|
||||
{
|
||||
struct task_struct *task = t ? t : current;
|
||||
|
||||
if ( ! (task->ptrace & PT_DTRACE) )
|
||||
return(0);
|
||||
|
||||
if (task->thread.singlestep_syscall)
|
||||
return(1);
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only x86 and x86_64 have an arch_align_stack().
|
||||
* All other arches have "#define arch_align_stack(x) (x)"
|
||||
* in their asm/system.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_int() % 8192;
|
||||
return sp & ~0xf;
|
||||
}
|
||||
#endif
|
||||
321
arch/um/kernel/ptrace.c
Normal file
321
arch/um/kernel/ptrace.c
Normal file
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/sched.h"
|
||||
#include "linux/mm.h"
|
||||
#include "linux/errno.h"
|
||||
#include "linux/smp_lock.h"
|
||||
#include "linux/security.h"
|
||||
#include "linux/ptrace.h"
|
||||
#include "linux/audit.h"
|
||||
#ifdef CONFIG_PROC_MM
|
||||
#include "linux/proc_mm.h"
|
||||
#endif
|
||||
#include "asm/ptrace.h"
|
||||
#include "asm/uaccess.h"
|
||||
#include "kern_util.h"
|
||||
#include "skas_ptrace.h"
|
||||
#include "sysdep/ptrace.h"
|
||||
#include "os.h"
|
||||
|
||||
static inline void set_singlestepping(struct task_struct *child, int on)
|
||||
{
|
||||
if (on)
|
||||
child->ptrace |= PT_DTRACE;
|
||||
else
|
||||
child->ptrace &= ~PT_DTRACE;
|
||||
child->thread.singlestep_syscall = 0;
|
||||
|
||||
#ifdef SUBARCH_SET_SINGLESTEPPING
|
||||
SUBARCH_SET_SINGLESTEPPING(child, on);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by kernel/ptrace.c when detaching..
|
||||
*/
|
||||
void ptrace_disable(struct task_struct *child)
|
||||
{
|
||||
set_singlestepping(child,0);
|
||||
}
|
||||
|
||||
extern int peek_user(struct task_struct * child, long addr, long data);
|
||||
extern int poke_user(struct task_struct * child, long addr, long data);
|
||||
|
||||
long arch_ptrace(struct task_struct *child, long request, long addr, long data)
|
||||
{
|
||||
int i, ret;
|
||||
unsigned long __user *p = (void __user *)(unsigned long)data;
|
||||
|
||||
switch (request) {
|
||||
/* when I and D space are separate, these will need to be fixed. */
|
||||
case PTRACE_PEEKTEXT: /* read word at location addr. */
|
||||
case PTRACE_PEEKDATA: {
|
||||
unsigned long tmp;
|
||||
int copied;
|
||||
|
||||
ret = -EIO;
|
||||
copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
|
||||
if (copied != sizeof(tmp))
|
||||
break;
|
||||
ret = put_user(tmp, p);
|
||||
break;
|
||||
}
|
||||
|
||||
/* read the word at location addr in the USER area. */
|
||||
case PTRACE_PEEKUSR:
|
||||
ret = peek_user(child, addr, data);
|
||||
break;
|
||||
|
||||
/* when I and D space are separate, this will have to be fixed. */
|
||||
case PTRACE_POKETEXT: /* write the word at location addr. */
|
||||
case PTRACE_POKEDATA:
|
||||
ret = -EIO;
|
||||
if (access_process_vm(child, addr, &data, sizeof(data),
|
||||
1) != sizeof(data))
|
||||
break;
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
|
||||
ret = poke_user(child, addr, data);
|
||||
break;
|
||||
|
||||
case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
|
||||
case PTRACE_CONT: { /* restart after signal. */
|
||||
ret = -EIO;
|
||||
if (!valid_signal(data))
|
||||
break;
|
||||
|
||||
set_singlestepping(child, 0);
|
||||
if (request == PTRACE_SYSCALL) {
|
||||
set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
|
||||
}
|
||||
else {
|
||||
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
|
||||
}
|
||||
child->exit_code = data;
|
||||
wake_up_process(child);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* make the child exit. Best I can do is send it a sigkill.
|
||||
* perhaps it should be put in the status that it wants to
|
||||
* exit.
|
||||
*/
|
||||
case PTRACE_KILL: {
|
||||
ret = 0;
|
||||
if (child->exit_state == EXIT_ZOMBIE) /* already dead */
|
||||
break;
|
||||
|
||||
set_singlestepping(child, 0);
|
||||
child->exit_code = SIGKILL;
|
||||
wake_up_process(child);
|
||||
break;
|
||||
}
|
||||
|
||||
case PTRACE_SINGLESTEP: { /* set the trap flag. */
|
||||
ret = -EIO;
|
||||
if (!valid_signal(data))
|
||||
break;
|
||||
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
|
||||
set_singlestepping(child, 1);
|
||||
child->exit_code = data;
|
||||
/* give it a chance to run. */
|
||||
wake_up_process(child);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case PTRACE_DETACH:
|
||||
/* detach a process that was attached. */
|
||||
ret = ptrace_detach(child, data);
|
||||
break;
|
||||
|
||||
#ifdef PTRACE_GETREGS
|
||||
case PTRACE_GETREGS: { /* Get all gp regs from the child. */
|
||||
if (!access_ok(VERIFY_WRITE, 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(VERIFY_READ, 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
|
||||
#ifdef PTRACE_GETFPREGS
|
||||
case PTRACE_GETFPREGS: /* Get the child FPU state. */
|
||||
ret = get_fpregs(data, child);
|
||||
break;
|
||||
#endif
|
||||
#ifdef PTRACE_SETFPREGS
|
||||
case PTRACE_SETFPREGS: /* Set the child FPU state. */
|
||||
ret = set_fpregs(data, child);
|
||||
break;
|
||||
#endif
|
||||
#ifdef PTRACE_GETFPXREGS
|
||||
case PTRACE_GETFPXREGS: /* Get the child FPU state. */
|
||||
ret = get_fpxregs(data, child);
|
||||
break;
|
||||
#endif
|
||||
#ifdef PTRACE_SETFPXREGS
|
||||
case PTRACE_SETFPXREGS: /* Set the child FPU state. */
|
||||
ret = set_fpxregs(data, child);
|
||||
break;
|
||||
#endif
|
||||
case PTRACE_GET_THREAD_AREA:
|
||||
ret = ptrace_get_thread_area(child, addr,
|
||||
(struct user_desc __user *) data);
|
||||
break;
|
||||
|
||||
case PTRACE_SET_THREAD_AREA:
|
||||
ret = ptrace_set_thread_area(child, addr,
|
||||
(struct user_desc __user *) data);
|
||||
break;
|
||||
|
||||
case PTRACE_FAULTINFO: {
|
||||
/* Take the info from thread->arch->faultinfo,
|
||||
* but transfer max. sizeof(struct ptrace_faultinfo).
|
||||
* On i386, ptrace_faultinfo is smaller!
|
||||
*/
|
||||
ret = copy_to_user(p, &child->thread.arch.faultinfo,
|
||||
sizeof(struct ptrace_faultinfo));
|
||||
if(ret)
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef PTRACE_LDT
|
||||
case PTRACE_LDT: {
|
||||
struct ptrace_ldt ldt;
|
||||
|
||||
if(copy_from_user(&ldt, p, sizeof(ldt))){
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
/* This one is confusing, so just punt and return -EIO for
|
||||
* now
|
||||
*/
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_PROC_MM
|
||||
case PTRACE_SWITCH_MM: {
|
||||
struct mm_struct *old = child->mm;
|
||||
struct mm_struct *new = proc_mm_get_mm(data);
|
||||
|
||||
if(IS_ERR(new)){
|
||||
ret = PTR_ERR(new);
|
||||
break;
|
||||
}
|
||||
|
||||
atomic_inc(&new->mm_users);
|
||||
child->mm = new;
|
||||
child->active_mm = new;
|
||||
mmput(old);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef PTRACE_ARCH_PRCTL
|
||||
case PTRACE_ARCH_PRCTL:
|
||||
/* XXX Calls ptrace on the host - needs some SMP thinking */
|
||||
ret = arch_prctl_skas(child, data, (void *) addr);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ret = ptrace_request(child, request, addr, data);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void send_sigtrap(struct task_struct *tsk, union uml_pt_regs *regs,
|
||||
int error_code)
|
||||
{
|
||||
struct siginfo info;
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.si_signo = SIGTRAP;
|
||||
info.si_code = TRAP_BRKPT;
|
||||
|
||||
/* User-mode eip? */
|
||||
info.si_addr = UPT_IS_USER(regs) ? (void __user *) UPT_IP(regs) : NULL;
|
||||
|
||||
/* Send us the fakey SIGTRAP */
|
||||
force_sig_info(SIGTRAP, &info, tsk);
|
||||
}
|
||||
|
||||
/* XXX Check PT_DTRACE vs TIF_SINGLESTEP for singlestepping check and
|
||||
* PT_PTRACED vs TIF_SYSCALL_TRACE for syscall tracing check
|
||||
*/
|
||||
void syscall_trace(union uml_pt_regs *regs, int entryexit)
|
||||
{
|
||||
int is_singlestep = (current->ptrace & PT_DTRACE) && entryexit;
|
||||
int tracesysgood;
|
||||
|
||||
if (unlikely(current->audit_context)) {
|
||||
if (!entryexit)
|
||||
audit_syscall_entry(HOST_AUDIT_ARCH,
|
||||
UPT_SYSCALL_NR(regs),
|
||||
UPT_SYSCALL_ARG1(regs),
|
||||
UPT_SYSCALL_ARG2(regs),
|
||||
UPT_SYSCALL_ARG3(regs),
|
||||
UPT_SYSCALL_ARG4(regs));
|
||||
else audit_syscall_exit(AUDITSC_RESULT(UPT_SYSCALL_RET(regs)),
|
||||
UPT_SYSCALL_RET(regs));
|
||||
}
|
||||
|
||||
/* Fake a debug trap */
|
||||
if (is_singlestep)
|
||||
send_sigtrap(current, regs, 0);
|
||||
|
||||
if (!test_thread_flag(TIF_SYSCALL_TRACE))
|
||||
return;
|
||||
|
||||
if (!(current->ptrace & PT_PTRACED))
|
||||
return;
|
||||
|
||||
/* the 0x80 provides a way for the tracing parent to distinguish
|
||||
between a syscall stop and SIGTRAP delivery */
|
||||
tracesysgood = (current->ptrace & PT_TRACESYSGOOD);
|
||||
ptrace_notify(SIGTRAP | (tracesysgood ? 0x80 : 0));
|
||||
|
||||
if (entryexit) /* force do_signal() --> is_syscall() */
|
||||
set_thread_flag(TIF_SIGPENDING);
|
||||
|
||||
/* this isn't the same as continuing with a signal, but it will do
|
||||
* for normal use. strace only continues with a signal if the
|
||||
* stopping signal is not SIGTRAP. -brl
|
||||
*/
|
||||
if (current->exit_code) {
|
||||
send_sig(current->exit_code, current, 1);
|
||||
current->exit_code = 0;
|
||||
}
|
||||
}
|
||||
64
arch/um/kernel/reboot.c
Normal file
64
arch/um/kernel/reboot.c
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2000, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/module.h"
|
||||
#include "linux/sched.h"
|
||||
#include "asm/smp.h"
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "kern.h"
|
||||
#include "os.h"
|
||||
#include "mode.h"
|
||||
#include "choose-mode.h"
|
||||
|
||||
void (*pm_power_off)(void);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static void kill_idlers(int me)
|
||||
{
|
||||
#ifdef CONFIG_MODE_TT
|
||||
struct task_struct *p;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < ARRAY_SIZE(idle_threads); i++){
|
||||
p = idle_threads[i];
|
||||
if((p != NULL) && (p->thread.mode.tt.extern_pid != me))
|
||||
os_kill_process(p->thread.mode.tt.extern_pid, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static void kill_off_processes(void)
|
||||
{
|
||||
CHOOSE_MODE(kill_off_processes_tt(), kill_off_processes_skas());
|
||||
#ifdef CONFIG_SMP
|
||||
kill_idlers(os_getpid());
|
||||
#endif
|
||||
}
|
||||
|
||||
void uml_cleanup(void)
|
||||
{
|
||||
kmalloc_ok = 0;
|
||||
do_uml_exitcalls();
|
||||
kill_off_processes();
|
||||
}
|
||||
|
||||
void machine_restart(char * __unused)
|
||||
{
|
||||
uml_cleanup();
|
||||
CHOOSE_MODE(reboot_tt(), reboot_skas());
|
||||
}
|
||||
|
||||
void machine_power_off(void)
|
||||
{
|
||||
uml_cleanup();
|
||||
CHOOSE_MODE(halt_tt(), halt_skas());
|
||||
}
|
||||
|
||||
void machine_halt(void)
|
||||
{
|
||||
machine_power_off();
|
||||
}
|
||||
56
arch/um/kernel/sigio.c
Normal file
56
arch/um/kernel/sigio.c
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/kernel.h"
|
||||
#include "linux/list.h"
|
||||
#include "linux/slab.h"
|
||||
#include "linux/signal.h"
|
||||
#include "linux/interrupt.h"
|
||||
#include "init.h"
|
||||
#include "sigio.h"
|
||||
#include "irq_user.h"
|
||||
#include "irq_kern.h"
|
||||
#include "os.h"
|
||||
|
||||
/* Protected by sigio_lock() called from write_sigio_workaround */
|
||||
static int sigio_irq_fd = -1;
|
||||
|
||||
static irqreturn_t sigio_interrupt(int irq, void *data)
|
||||
{
|
||||
char c;
|
||||
|
||||
os_read_file(sigio_irq_fd, &c, sizeof(c));
|
||||
reactivate_fd(sigio_irq_fd, SIGIO_WRITE_IRQ);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int write_sigio_irq(int fd)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = um_request_irq(SIGIO_WRITE_IRQ, fd, IRQ_READ, sigio_interrupt,
|
||||
IRQF_DISABLED|IRQF_SAMPLE_RANDOM, "write sigio",
|
||||
NULL);
|
||||
if(err){
|
||||
printk("write_sigio_irq : um_request_irq failed, err = %d\n",
|
||||
err);
|
||||
return -1;
|
||||
}
|
||||
sigio_irq_fd = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* These are called from os-Linux/sigio.c to protect its pollfds arrays. */
|
||||
static DEFINE_SPINLOCK(sigio_spinlock);
|
||||
|
||||
void sigio_lock(void)
|
||||
{
|
||||
spin_lock(&sigio_spinlock);
|
||||
}
|
||||
|
||||
void sigio_unlock(void)
|
||||
{
|
||||
spin_unlock(&sigio_spinlock);
|
||||
}
|
||||
190
arch/um/kernel/signal.c
Normal file
190
arch/um/kernel/signal.c
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/stddef.h"
|
||||
#include "linux/sys.h"
|
||||
#include "linux/sched.h"
|
||||
#include "linux/wait.h"
|
||||
#include "linux/kernel.h"
|
||||
#include "linux/smp_lock.h"
|
||||
#include "linux/module.h"
|
||||
#include "linux/slab.h"
|
||||
#include "linux/tty.h"
|
||||
#include "linux/binfmts.h"
|
||||
#include "linux/ptrace.h"
|
||||
#include "asm/signal.h"
|
||||
#include "asm/uaccess.h"
|
||||
#include "asm/unistd.h"
|
||||
#include "user_util.h"
|
||||
#include "asm/ucontext.h"
|
||||
#include "kern_util.h"
|
||||
#include "signal_kern.h"
|
||||
#include "kern.h"
|
||||
#include "frame_kern.h"
|
||||
#include "sigcontext.h"
|
||||
#include "mode.h"
|
||||
|
||||
EXPORT_SYMBOL(block_signals);
|
||||
EXPORT_SYMBOL(unblock_signals);
|
||||
|
||||
#define _S(nr) (1<<((nr)-1))
|
||||
|
||||
#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
|
||||
|
||||
/*
|
||||
* OK, we're invoking a handler
|
||||
*/
|
||||
static int handle_signal(struct pt_regs *regs, unsigned long signr,
|
||||
struct k_sigaction *ka, siginfo_t *info,
|
||||
sigset_t *oldset)
|
||||
{
|
||||
unsigned long sp;
|
||||
int err;
|
||||
|
||||
/* Always make any pending restarted system calls return -EINTR */
|
||||
current_thread_info()->restart_block.fn = do_no_restart_syscall;
|
||||
|
||||
/* 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 (!(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((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(!(ka->sa.sa_flags & SA_SIGINFO))
|
||||
err = setup_signal_stack_sc(sp, signr, ka, regs, oldset);
|
||||
else
|
||||
#endif
|
||||
err = setup_signal_stack_si(sp, signr, ka, regs, info, oldset);
|
||||
|
||||
if(err){
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
current->blocked = *oldset;
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
force_sigsegv(signr, current);
|
||||
} else {
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
sigorsets(¤t->blocked, ¤t->blocked,
|
||||
&ka->sa.sa_mask);
|
||||
if(!(ka->sa.sa_flags & SA_NODEFER))
|
||||
sigaddset(¤t->blocked, signr);
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int kern_do_signal(struct pt_regs *regs)
|
||||
{
|
||||
struct k_sigaction ka_copy;
|
||||
siginfo_t info;
|
||||
sigset_t *oldset;
|
||||
int sig, handled_sig = 0;
|
||||
|
||||
if (test_thread_flag(TIF_RESTORE_SIGMASK))
|
||||
oldset = ¤t->saved_sigmask;
|
||||
else
|
||||
oldset = ¤t->blocked;
|
||||
|
||||
while((sig = get_signal_to_deliver(&info, &ka_copy, regs, NULL)) > 0){
|
||||
handled_sig = 1;
|
||||
/* Whee! Actually deliver the signal. */
|
||||
if(!handle_signal(regs, sig, &ka_copy, &info, oldset)){
|
||||
/* a signal was successfully delivered; the saved
|
||||
* sigmask will have been stored in the signal frame,
|
||||
* and will be restored by sigreturn, so we can simply
|
||||
* clear the TIF_RESTORE_SIGMASK flag */
|
||||
if (test_thread_flag(TIF_RESTORE_SIGMASK))
|
||||
clear_thread_flag(TIF_RESTORE_SIGMASK);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* This closes a way to execute a system call on the host. If
|
||||
* you set a breakpoint on a system call instruction and singlestep
|
||||
* from it, the tracing thread used to PTRACE_SINGLESTEP the process
|
||||
* rather than PTRACE_SYSCALL it, allowing the system call to execute
|
||||
* on the host. The tracing thread will check this flag and
|
||||
* PTRACE_SYSCALL if necessary.
|
||||
*/
|
||||
if(current->ptrace & PT_DTRACE)
|
||||
current->thread.singlestep_syscall =
|
||||
is_syscall(PT_REGS_IP(¤t->thread.regs));
|
||||
|
||||
/* if there's no signal to deliver, we just put the saved sigmask
|
||||
* back */
|
||||
if (!handled_sig && test_thread_flag(TIF_RESTORE_SIGMASK)) {
|
||||
clear_thread_flag(TIF_RESTORE_SIGMASK);
|
||||
sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL);
|
||||
}
|
||||
return handled_sig;
|
||||
}
|
||||
|
||||
int do_signal(void)
|
||||
{
|
||||
return kern_do_signal(¤t->thread.regs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Atomically swap in the new signal mask, and wait for a signal.
|
||||
*/
|
||||
long sys_sigsuspend(int history0, int history1, old_sigset_t mask)
|
||||
{
|
||||
mask &= _BLOCKABLE;
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
current->saved_sigmask = current->blocked;
|
||||
siginitset(¤t->blocked, mask);
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
|
||||
current->state = TASK_INTERRUPTIBLE;
|
||||
schedule();
|
||||
set_thread_flag(TIF_RESTORE_SIGMASK);
|
||||
return -ERESTARTNOHAND;
|
||||
}
|
||||
|
||||
long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss)
|
||||
{
|
||||
return do_sigaltstack(uss, uoss, PT_REGS_SP(¤t->thread.regs));
|
||||
}
|
||||
15
arch/um/kernel/skas/Makefile
Normal file
15
arch/um/kernel/skas/Makefile
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
# Copyright (C) 2002 - 2004 Jeff Dike (jdike@addtoit.com)
|
||||
# Licensed under the GPL
|
||||
#
|
||||
|
||||
obj-y := clone.o exec.o mem.o mmu.o process.o syscall.o tlb.o uaccess.o
|
||||
|
||||
# clone.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_clone.o := $(CFLAGS_NO_HARDENING)
|
||||
UNPROFILE_OBJS := clone.o
|
||||
|
||||
include arch/um/scripts/Makefile.rules
|
||||
54
arch/um/kernel/skas/clone.c
Normal file
54
arch/um/kernel/skas/clone.c
Normal file
@@ -0,0 +1,54 @@
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/time.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/page.h>
|
||||
#include "ptrace_user.h"
|
||||
#include "skas.h"
|
||||
#include "stub-data.h"
|
||||
#include "uml-config.h"
|
||||
#include "sysdep/stub.h"
|
||||
#include "kern_constants.h"
|
||||
|
||||
/* This is in a separate file because it needs to be compiled with any
|
||||
* extraneous gcc flags (-pg, -fprofile-arcs, -ftest-coverage) disabled
|
||||
*
|
||||
* Use UM_KERN_PAGE_SIZE instead of PAGE_SIZE because that calls getpagesize
|
||||
* on some systems.
|
||||
*/
|
||||
|
||||
void __attribute__ ((__section__ (".__syscall_stub")))
|
||||
stub_clone_handler(void)
|
||||
{
|
||||
struct stub_data *data = (struct stub_data *) UML_CONFIG_STUB_DATA;
|
||||
long err;
|
||||
|
||||
err = stub_syscall2(__NR_clone, CLONE_PARENT | CLONE_FILES | SIGCHLD,
|
||||
UML_CONFIG_STUB_DATA + UM_KERN_PAGE_SIZE / 2 -
|
||||
sizeof(void *));
|
||||
if(err != 0)
|
||||
goto out;
|
||||
|
||||
err = stub_syscall4(__NR_ptrace, PTRACE_TRACEME, 0, 0, 0);
|
||||
if(err)
|
||||
goto out;
|
||||
|
||||
err = stub_syscall3(__NR_setitimer, ITIMER_VIRTUAL,
|
||||
(long) &data->timer, 0);
|
||||
if(err)
|
||||
goto out;
|
||||
|
||||
remap_stack(data->fd, data->offset);
|
||||
goto done;
|
||||
|
||||
out:
|
||||
/* save current result.
|
||||
* Parent: pid;
|
||||
* child: retcode of mmap already saved and it jumps around this
|
||||
* assignment
|
||||
*/
|
||||
data->err = err;
|
||||
done:
|
||||
trap_myself();
|
||||
}
|
||||
30
arch/um/kernel/skas/exec.c
Normal file
30
arch/um/kernel/skas/exec.c
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/kernel.h"
|
||||
#include "asm/current.h"
|
||||
#include "asm/page.h"
|
||||
#include "asm/signal.h"
|
||||
#include "asm/ptrace.h"
|
||||
#include "asm/uaccess.h"
|
||||
#include "asm/mmu_context.h"
|
||||
#include "tlb.h"
|
||||
#include "skas.h"
|
||||
#include "um_mmu.h"
|
||||
#include "os.h"
|
||||
|
||||
void flush_thread_skas(void)
|
||||
{
|
||||
force_flush_all();
|
||||
switch_mm_skas(¤t->mm->context.skas.id);
|
||||
}
|
||||
|
||||
void start_thread_skas(struct pt_regs *regs, unsigned long eip,
|
||||
unsigned long esp)
|
||||
{
|
||||
set_fs(USER_DS);
|
||||
PT_REGS_IP(regs) = eip;
|
||||
PT_REGS_SP(regs) = esp;
|
||||
}
|
||||
22
arch/um/kernel/skas/mem.c
Normal file
22
arch/um/kernel/skas/mem.c
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/mm.h"
|
||||
#include "asm/pgtable.h"
|
||||
#include "mem_user.h"
|
||||
#include "skas.h"
|
||||
|
||||
unsigned long set_task_sizes_skas(unsigned long *task_size_out)
|
||||
{
|
||||
/* Round up to the nearest 4M */
|
||||
unsigned long host_task_size = ROUND_4M((unsigned long)
|
||||
&host_task_size);
|
||||
|
||||
if (!skas_needs_stub)
|
||||
*task_size_out = host_task_size;
|
||||
else *task_size_out = CONFIG_STUB_START & PGDIR_MASK;
|
||||
|
||||
return host_task_size;
|
||||
}
|
||||
158
arch/um/kernel/skas/mmu.c
Normal file
158
arch/um/kernel/skas/mmu.c
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/sched.h"
|
||||
#include "linux/list.h"
|
||||
#include "linux/spinlock.h"
|
||||
#include "linux/slab.h"
|
||||
#include "linux/errno.h"
|
||||
#include "linux/mm.h"
|
||||
#include "asm/current.h"
|
||||
#include "asm/segment.h"
|
||||
#include "asm/mmu.h"
|
||||
#include "asm/pgalloc.h"
|
||||
#include "asm/pgtable.h"
|
||||
#include "asm/ldt.h"
|
||||
#include "os.h"
|
||||
#include "skas.h"
|
||||
|
||||
extern int __syscall_stub_start;
|
||||
|
||||
static int init_stub_pte(struct mm_struct *mm, unsigned long proc,
|
||||
unsigned long kernel)
|
||||
{
|
||||
pgd_t *pgd;
|
||||
pud_t *pud;
|
||||
pmd_t *pmd;
|
||||
pte_t *pte;
|
||||
|
||||
pgd = pgd_offset(mm, proc);
|
||||
pud = pud_alloc(mm, pgd, proc);
|
||||
if (!pud)
|
||||
goto out;
|
||||
|
||||
pmd = pmd_alloc(mm, pud, proc);
|
||||
if (!pmd)
|
||||
goto out_pmd;
|
||||
|
||||
pte = pte_alloc_map(mm, pmd, proc);
|
||||
if (!pte)
|
||||
goto out_pte;
|
||||
|
||||
/* There's an interaction between the skas0 stub pages, stack
|
||||
* randomization, and the BUG at the end of exit_mmap. exit_mmap
|
||||
* checks that the number of page tables freed is the same as had
|
||||
* been allocated. If the stack is on the last page table page,
|
||||
* then the stack pte page will be freed, and if not, it won't. To
|
||||
* avoid having to know where the stack is, or if the process mapped
|
||||
* something at the top of its address space for some other reason,
|
||||
* we set TASK_SIZE to end at the start of the last page table.
|
||||
* This keeps exit_mmap off the last page, but introduces a leak
|
||||
* of that page. So, we hang onto it here and free it in
|
||||
* destroy_context_skas.
|
||||
*/
|
||||
|
||||
mm->context.skas.last_page_table = pmd_page_vaddr(*pmd);
|
||||
#ifdef CONFIG_3_LEVEL_PGTABLES
|
||||
mm->context.skas.last_pmd = (unsigned long) __va(pud_val(*pud));
|
||||
#endif
|
||||
|
||||
*pte = mk_pte(virt_to_page(kernel), __pgprot(_PAGE_PRESENT));
|
||||
*pte = pte_mkread(*pte);
|
||||
return(0);
|
||||
|
||||
out_pmd:
|
||||
pud_free(pud);
|
||||
out_pte:
|
||||
pmd_free(pmd);
|
||||
out:
|
||||
return(-ENOMEM);
|
||||
}
|
||||
|
||||
int init_new_context_skas(struct task_struct *task, struct mm_struct *mm)
|
||||
{
|
||||
struct mmu_context_skas *from_mm = NULL;
|
||||
struct mmu_context_skas *to_mm = &mm->context.skas;
|
||||
unsigned long stack = 0;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
if(skas_needs_stub){
|
||||
stack = get_zeroed_page(GFP_KERNEL);
|
||||
if(stack == 0)
|
||||
goto out;
|
||||
|
||||
/* This zeros the entry that pgd_alloc didn't, needed since
|
||||
* we are about to reinitialize it, and want mm.nr_ptes to
|
||||
* be accurate.
|
||||
*/
|
||||
mm->pgd[USER_PTRS_PER_PGD] = __pgd(0);
|
||||
|
||||
ret = init_stub_pte(mm, CONFIG_STUB_CODE,
|
||||
(unsigned long) &__syscall_stub_start);
|
||||
if(ret)
|
||||
goto out_free;
|
||||
|
||||
ret = init_stub_pte(mm, CONFIG_STUB_DATA, stack);
|
||||
if(ret)
|
||||
goto out_free;
|
||||
|
||||
mm->nr_ptes--;
|
||||
}
|
||||
|
||||
to_mm->id.stack = stack;
|
||||
if(current->mm != NULL && current->mm != &init_mm)
|
||||
from_mm = ¤t->mm->context.skas;
|
||||
|
||||
if(proc_mm){
|
||||
ret = new_mm(stack);
|
||||
if(ret < 0){
|
||||
printk("init_new_context_skas - new_mm failed, "
|
||||
"errno = %d\n", ret);
|
||||
goto out_free;
|
||||
}
|
||||
to_mm->id.u.mm_fd = ret;
|
||||
}
|
||||
else {
|
||||
if(from_mm)
|
||||
to_mm->id.u.pid = copy_context_skas0(stack,
|
||||
from_mm->id.u.pid);
|
||||
else to_mm->id.u.pid = start_userspace(stack);
|
||||
}
|
||||
|
||||
ret = init_new_ldt(to_mm, from_mm);
|
||||
if(ret < 0){
|
||||
printk("init_new_context_skas - init_ldt"
|
||||
" failed, errno = %d\n", ret);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_free:
|
||||
if(to_mm->id.stack != 0)
|
||||
free_page(to_mm->id.stack);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void destroy_context_skas(struct mm_struct *mm)
|
||||
{
|
||||
struct mmu_context_skas *mmu = &mm->context.skas;
|
||||
|
||||
if(proc_mm)
|
||||
os_close_file(mmu->id.u.mm_fd);
|
||||
else
|
||||
os_kill_ptraced_process(mmu->id.u.pid, 1);
|
||||
|
||||
if(!proc_mm || !ptrace_faultinfo){
|
||||
free_page(mmu->id.stack);
|
||||
pte_lock_deinit(virt_to_page(mmu->last_page_table));
|
||||
pte_free_kernel((pte_t *) mmu->last_page_table);
|
||||
dec_zone_page_state(virt_to_page(mmu->last_page_table), NR_PAGETABLE);
|
||||
#ifdef CONFIG_3_LEVEL_PGTABLES
|
||||
pmd_free((pmd_t *) mmu->last_pmd);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
217
arch/um/kernel/skas/process.c
Normal file
217
arch/um/kernel/skas/process.c
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/sched.h"
|
||||
#include "linux/slab.h"
|
||||
#include "linux/ptrace.h"
|
||||
#include "linux/proc_fs.h"
|
||||
#include "linux/file.h"
|
||||
#include "linux/errno.h"
|
||||
#include "linux/init.h"
|
||||
#include "asm/uaccess.h"
|
||||
#include "asm/atomic.h"
|
||||
#include "kern_util.h"
|
||||
#include "skas.h"
|
||||
#include "os.h"
|
||||
#include "user_util.h"
|
||||
#include "tlb.h"
|
||||
#include "kern.h"
|
||||
#include "mode.h"
|
||||
#include "registers.h"
|
||||
|
||||
void switch_to_skas(void *prev, void *next)
|
||||
{
|
||||
struct task_struct *from, *to;
|
||||
|
||||
from = prev;
|
||||
to = next;
|
||||
|
||||
/* XXX need to check runqueues[cpu].idle */
|
||||
if(current->pid == 0)
|
||||
switch_timers(0);
|
||||
|
||||
switch_threads(&from->thread.mode.skas.switch_buf,
|
||||
&to->thread.mode.skas.switch_buf);
|
||||
|
||||
arch_switch_to_skas(current->thread.prev_sched, current);
|
||||
|
||||
if(current->pid == 0)
|
||||
switch_timers(1);
|
||||
}
|
||||
|
||||
extern void schedule_tail(struct task_struct *prev);
|
||||
|
||||
/* 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 *), n;
|
||||
void *arg;
|
||||
|
||||
if(current->thread.prev_sched != NULL)
|
||||
schedule_tail(current->thread.prev_sched);
|
||||
current->thread.prev_sched = NULL;
|
||||
|
||||
fn = current->thread.request.u.thread.proc;
|
||||
arg = current->thread.request.u.thread.arg;
|
||||
|
||||
/* The return value is 1 if the kernel thread execs a process,
|
||||
* 0 if it just exits
|
||||
*/
|
||||
n = run_kernel_thread(fn, arg, ¤t->thread.exec_buf);
|
||||
if(n == 1){
|
||||
/* Handle any immediate reschedules or signals */
|
||||
interrupt_end();
|
||||
userspace(¤t->thread.regs.regs);
|
||||
}
|
||||
else do_exit(0);
|
||||
}
|
||||
|
||||
void release_thread_skas(struct task_struct *task)
|
||||
{
|
||||
}
|
||||
|
||||
/* Called magically, see new_thread_handler above */
|
||||
void fork_handler(void)
|
||||
{
|
||||
force_flush_all();
|
||||
if(current->thread.prev_sched == NULL)
|
||||
panic("blech");
|
||||
|
||||
schedule_tail(current->thread.prev_sched);
|
||||
|
||||
/* XXX: if interrupt_end() calls schedule, this call to
|
||||
* arch_switch_to_skas isn't needed. We could want to apply this to
|
||||
* improve performance. -bb */
|
||||
arch_switch_to_skas(current->thread.prev_sched, current);
|
||||
|
||||
current->thread.prev_sched = NULL;
|
||||
|
||||
/* Handle any immediate reschedules or signals */
|
||||
interrupt_end();
|
||||
|
||||
userspace(¤t->thread.regs.regs);
|
||||
}
|
||||
|
||||
int copy_thread_skas(int nr, unsigned long clone_flags, unsigned long sp,
|
||||
unsigned long stack_top, struct task_struct * p,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
void (*handler)(void);
|
||||
|
||||
if(current->thread.forking){
|
||||
memcpy(&p->thread.regs.regs.skas, ®s->regs.skas,
|
||||
sizeof(p->thread.regs.regs.skas));
|
||||
REGS_SET_SYSCALL_RETURN(p->thread.regs.regs.skas.regs, 0);
|
||||
if(sp != 0) REGS_SP(p->thread.regs.regs.skas.regs) = sp;
|
||||
|
||||
handler = fork_handler;
|
||||
|
||||
arch_copy_thread(¤t->thread.arch, &p->thread.arch);
|
||||
}
|
||||
else {
|
||||
init_thread_registers(&p->thread.regs.regs);
|
||||
p->thread.request.u.thread = current->thread.request.u.thread;
|
||||
handler = new_thread_handler;
|
||||
}
|
||||
|
||||
new_thread(task_stack_page(p), &p->thread.mode.skas.switch_buf,
|
||||
handler);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int new_mm(unsigned long stack)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = os_open_file("/proc/mm", of_cloexec(of_write(OPENFLAGS())), 0);
|
||||
if(fd < 0)
|
||||
return(fd);
|
||||
|
||||
if(skas_needs_stub)
|
||||
map_stub_pages(fd, CONFIG_STUB_CODE, CONFIG_STUB_DATA, stack);
|
||||
|
||||
return(fd);
|
||||
}
|
||||
|
||||
void init_idle_skas(void)
|
||||
{
|
||||
cpu_tasks[current_thread->cpu].pid = os_getpid();
|
||||
default_idle();
|
||||
}
|
||||
|
||||
extern void start_kernel(void);
|
||||
|
||||
static int start_kernel_proc(void *unused)
|
||||
{
|
||||
int pid;
|
||||
|
||||
block_signals();
|
||||
pid = os_getpid();
|
||||
|
||||
cpu_tasks[0].pid = pid;
|
||||
cpu_tasks[0].task = current;
|
||||
#ifdef CONFIG_SMP
|
||||
cpu_online_map = cpumask_of_cpu(0);
|
||||
#endif
|
||||
start_kernel();
|
||||
return(0);
|
||||
}
|
||||
|
||||
extern int userspace_pid[];
|
||||
|
||||
int start_uml_skas(void)
|
||||
{
|
||||
if(proc_mm)
|
||||
userspace_pid[0] = start_userspace(0);
|
||||
|
||||
init_new_thread_signals();
|
||||
|
||||
init_task.thread.request.u.thread.proc = start_kernel_proc;
|
||||
init_task.thread.request.u.thread.arg = NULL;
|
||||
return(start_idle_thread(task_stack_page(&init_task),
|
||||
&init_task.thread.mode.skas.switch_buf));
|
||||
}
|
||||
|
||||
int external_pid_skas(struct task_struct *task)
|
||||
{
|
||||
#warning Need to look up userspace_pid by cpu
|
||||
return(userspace_pid[0]);
|
||||
}
|
||||
|
||||
int thread_pid_skas(struct task_struct *task)
|
||||
{
|
||||
#warning Need to look up userspace_pid by cpu
|
||||
return(userspace_pid[0]);
|
||||
}
|
||||
|
||||
void kill_off_processes_skas(void)
|
||||
{
|
||||
if(proc_mm)
|
||||
#warning need to loop over userspace_pids in kill_off_processes_skas
|
||||
os_kill_ptraced_process(userspace_pid[0], 1);
|
||||
else {
|
||||
struct task_struct *p;
|
||||
int pid, me;
|
||||
|
||||
me = os_getpid();
|
||||
for_each_process(p){
|
||||
if(p->mm == NULL)
|
||||
continue;
|
||||
|
||||
pid = p->mm->context.skas.id.u.pid;
|
||||
os_kill_ptraced_process(pid, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long current_stub_stack(void)
|
||||
{
|
||||
if(current->mm == NULL)
|
||||
return(0);
|
||||
|
||||
return(current->mm->context.skas.id.stack);
|
||||
}
|
||||
43
arch/um/kernel/skas/syscall.c
Normal file
43
arch/um/kernel/skas/syscall.c
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/sys.h"
|
||||
#include "linux/ptrace.h"
|
||||
#include "asm/errno.h"
|
||||
#include "asm/unistd.h"
|
||||
#include "asm/ptrace.h"
|
||||
#include "asm/current.h"
|
||||
#include "sysdep/syscalls.h"
|
||||
#include "kern_util.h"
|
||||
#include "syscall.h"
|
||||
|
||||
void handle_syscall(union uml_pt_regs *r)
|
||||
{
|
||||
struct pt_regs *regs = container_of(r, struct pt_regs, regs);
|
||||
long result;
|
||||
int syscall;
|
||||
|
||||
syscall_trace(r, 0);
|
||||
|
||||
current->thread.nsyscalls++;
|
||||
nsyscalls++;
|
||||
|
||||
/* This should go in the declaration of syscall, but when I do that,
|
||||
* strace -f -c bash -c 'ls ; ls' breaks, sometimes not tracing
|
||||
* children at all, sometimes hanging when bash doesn't see the first
|
||||
* ls exit.
|
||||
* The assembly looks functionally the same to me. This is
|
||||
* gcc version 4.0.1 20050727 (Red Hat 4.0.1-5)
|
||||
* in case it's a compiler bug.
|
||||
*/
|
||||
syscall = UPT_SYSCALL_NR(r);
|
||||
if((syscall >= NR_syscalls) || (syscall < 0))
|
||||
result = -ENOSYS;
|
||||
else result = EXECUTE_SYSCALL(syscall, regs);
|
||||
|
||||
REGS_SET_SYSCALL_RETURN(r->skas.regs, result);
|
||||
|
||||
syscall_trace(r, 1);
|
||||
}
|
||||
97
arch/um/kernel/skas/tlb.c
Normal file
97
arch/um/kernel/skas/tlb.c
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Copyright 2003 PathScale, Inc.
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/stddef.h"
|
||||
#include "linux/sched.h"
|
||||
#include "linux/mm.h"
|
||||
#include "asm/page.h"
|
||||
#include "asm/pgtable.h"
|
||||
#include "asm/mmu.h"
|
||||
#include "user_util.h"
|
||||
#include "mem_user.h"
|
||||
#include "mem.h"
|
||||
#include "skas.h"
|
||||
#include "os.h"
|
||||
#include "tlb.h"
|
||||
|
||||
static int do_ops(union mm_context *mmu, struct host_vm_op *ops, int last,
|
||||
int finished, void **flush)
|
||||
{
|
||||
struct host_vm_op *op;
|
||||
int i, ret = 0;
|
||||
|
||||
for(i = 0; i <= last && !ret; i++){
|
||||
op = &ops[i];
|
||||
switch(op->type){
|
||||
case MMAP:
|
||||
ret = map(&mmu->skas.id, op->u.mmap.addr,
|
||||
op->u.mmap.len, op->u.mmap.r, op->u.mmap.w,
|
||||
op->u.mmap.x, op->u.mmap.fd,
|
||||
op->u.mmap.offset, finished, flush);
|
||||
break;
|
||||
case MUNMAP:
|
||||
ret = unmap(&mmu->skas.id,
|
||||
(void *) op->u.munmap.addr,
|
||||
op->u.munmap.len, finished, flush);
|
||||
break;
|
||||
case MPROTECT:
|
||||
ret = protect(&mmu->skas.id, op->u.mprotect.addr,
|
||||
op->u.mprotect.len, op->u.mprotect.r,
|
||||
op->u.mprotect.w, op->u.mprotect.x,
|
||||
finished, flush);
|
||||
break;
|
||||
default:
|
||||
printk("Unknown op type %d in do_ops\n", op->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern int proc_mm;
|
||||
|
||||
static void fix_range(struct mm_struct *mm, unsigned long start_addr,
|
||||
unsigned long end_addr, int force)
|
||||
{
|
||||
if(!proc_mm && (end_addr > CONFIG_STUB_START))
|
||||
end_addr = CONFIG_STUB_START;
|
||||
|
||||
fix_range_common(mm, start_addr, end_addr, force, do_ops);
|
||||
}
|
||||
|
||||
void __flush_tlb_one_skas(unsigned long addr)
|
||||
{
|
||||
flush_tlb_kernel_range_common(addr, addr + PAGE_SIZE);
|
||||
}
|
||||
|
||||
void flush_tlb_range_skas(struct vm_area_struct *vma, unsigned long start,
|
||||
unsigned long end)
|
||||
{
|
||||
if(vma->vm_mm == NULL)
|
||||
flush_tlb_kernel_range_common(start, end);
|
||||
else fix_range(vma->vm_mm, start, end, 0);
|
||||
}
|
||||
|
||||
void flush_tlb_mm_skas(struct mm_struct *mm)
|
||||
{
|
||||
unsigned long end;
|
||||
|
||||
/* Don't bother flushing if this address space is about to be
|
||||
* destroyed.
|
||||
*/
|
||||
if(atomic_read(&mm->mm_users) == 0)
|
||||
return;
|
||||
|
||||
end = proc_mm ? task_size : CONFIG_STUB_START;
|
||||
fix_range(mm, 0, end, 0);
|
||||
}
|
||||
|
||||
void force_flush_all_skas(void)
|
||||
{
|
||||
unsigned long end = proc_mm ? task_size : CONFIG_STUB_START;
|
||||
fix_range(current->mm, 0, end, 1);
|
||||
}
|
||||
265
arch/um/kernel/skas/uaccess.c
Normal file
265
arch/um/kernel/skas/uaccess.c
Normal file
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/compiler.h"
|
||||
#include "linux/stddef.h"
|
||||
#include "linux/kernel.h"
|
||||
#include "linux/string.h"
|
||||
#include "linux/fs.h"
|
||||
#include "linux/hardirq.h"
|
||||
#include "linux/highmem.h"
|
||||
#include "asm/page.h"
|
||||
#include "asm/pgtable.h"
|
||||
#include "asm/uaccess.h"
|
||||
#include "kern_util.h"
|
||||
#include "os.h"
|
||||
|
||||
extern void *um_virt_to_phys(struct task_struct *task, unsigned long addr,
|
||||
pte_t *pte_out);
|
||||
|
||||
static unsigned long maybe_map(unsigned long virt, int is_write)
|
||||
{
|
||||
pte_t pte;
|
||||
int err;
|
||||
|
||||
void *phys = um_virt_to_phys(current, virt, &pte);
|
||||
int dummy_code;
|
||||
|
||||
if(IS_ERR(phys) || (is_write && !pte_write(pte))){
|
||||
err = handle_page_fault(virt, 0, is_write, 1, &dummy_code);
|
||||
if(err)
|
||||
return(-1UL);
|
||||
phys = um_virt_to_phys(current, virt, NULL);
|
||||
}
|
||||
if(IS_ERR(phys))
|
||||
phys = (void *) -1;
|
||||
|
||||
return((unsigned long) phys);
|
||||
}
|
||||
|
||||
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;
|
||||
int n;
|
||||
|
||||
addr = maybe_map(addr, is_write);
|
||||
if(addr == -1UL)
|
||||
return(-1);
|
||||
|
||||
page = phys_to_page(addr);
|
||||
addr = (unsigned long) kmap_atomic(page, KM_UML_USERCOPY) + (addr & ~PAGE_MASK);
|
||||
|
||||
n = (*op)(addr, len, arg);
|
||||
|
||||
kunmap_atomic(page, KM_UML_USERCOPY);
|
||||
|
||||
return(n);
|
||||
}
|
||||
|
||||
static void do_buffer_op(void *jmpbuf, void *arg_ptr)
|
||||
{
|
||||
va_list args;
|
||||
unsigned long addr;
|
||||
int len, is_write, size, remain, n;
|
||||
int (*op)(unsigned long, int, void *);
|
||||
void *arg;
|
||||
int *res;
|
||||
|
||||
va_copy(args, *(va_list *)arg_ptr);
|
||||
addr = va_arg(args, unsigned long);
|
||||
len = va_arg(args, int);
|
||||
is_write = va_arg(args, int);
|
||||
op = va_arg(args, void *);
|
||||
arg = va_arg(args, void *);
|
||||
res = va_arg(args, int *);
|
||||
va_end(args);
|
||||
size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len);
|
||||
remain = len;
|
||||
|
||||
current->thread.fault_catcher = jmpbuf;
|
||||
n = do_op_one_page(addr, size, is_write, op, arg);
|
||||
if(n != 0){
|
||||
*res = (n < 0 ? remain : 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
addr += size;
|
||||
remain -= size;
|
||||
if(remain == 0){
|
||||
*res = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
while(addr < ((addr + remain) & PAGE_MASK)){
|
||||
n = do_op_one_page(addr, PAGE_SIZE, is_write, op, arg);
|
||||
if(n != 0){
|
||||
*res = (n < 0 ? remain : 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
addr += PAGE_SIZE;
|
||||
remain -= PAGE_SIZE;
|
||||
}
|
||||
if(remain == 0){
|
||||
*res = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
n = do_op_one_page(addr, remain, is_write, op, arg);
|
||||
if(n != 0)
|
||||
*res = (n < 0 ? remain : 0);
|
||||
else *res = 0;
|
||||
out:
|
||||
current->thread.fault_catcher = NULL;
|
||||
}
|
||||
|
||||
static int buffer_op(unsigned long addr, int len, int is_write,
|
||||
int (*op)(unsigned long addr, int len, void *arg),
|
||||
void *arg)
|
||||
{
|
||||
int faulted, res;
|
||||
|
||||
faulted = setjmp_wrapper(do_buffer_op, addr, len, is_write, op, arg,
|
||||
&res);
|
||||
if(!faulted)
|
||||
return(res);
|
||||
|
||||
return(addr + len - (unsigned long) current->thread.fault_addr);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int copy_from_user_skas(void *to, const void __user *from, int n)
|
||||
{
|
||||
if(segment_eq(get_fs(), KERNEL_DS)){
|
||||
memcpy(to, (__force void*)from, n);
|
||||
return(0);
|
||||
}
|
||||
|
||||
return(access_ok(VERIFY_READ, from, n) ?
|
||||
buffer_op((unsigned long) from, n, 0, copy_chunk_from_user, &to):
|
||||
n);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int copy_to_user_skas(void __user *to, const void *from, int n)
|
||||
{
|
||||
if(segment_eq(get_fs(), KERNEL_DS)){
|
||||
memcpy((__force void*)to, from, n);
|
||||
return(0);
|
||||
}
|
||||
|
||||
return(access_ok(VERIFY_WRITE, to, n) ?
|
||||
buffer_op((unsigned long) to, n, 1, copy_chunk_to_user, &from) :
|
||||
n);
|
||||
}
|
||||
|
||||
static int strncpy_chunk_from_user(unsigned long from, int len, void *arg)
|
||||
{
|
||||
char **to_ptr = arg, *to = *to_ptr;
|
||||
int n;
|
||||
|
||||
strncpy(to, (void *) from, len);
|
||||
n = strnlen(to, len);
|
||||
*to_ptr += n;
|
||||
|
||||
if(n < len)
|
||||
return(1);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int strncpy_from_user_skas(char *dst, const char __user *src, int count)
|
||||
{
|
||||
int n;
|
||||
char *ptr = dst;
|
||||
|
||||
if(segment_eq(get_fs(), KERNEL_DS)){
|
||||
strncpy(dst, (__force void*)src, count);
|
||||
return(strnlen(dst, count));
|
||||
}
|
||||
|
||||
if(!access_ok(VERIFY_READ, 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));
|
||||
}
|
||||
|
||||
static int clear_chunk(unsigned long addr, int len, void *unused)
|
||||
{
|
||||
memset((void *) addr, 0, len);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int __clear_user_skas(void __user *mem, int len)
|
||||
{
|
||||
return(buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL));
|
||||
}
|
||||
|
||||
int clear_user_skas(void __user *mem, int len)
|
||||
{
|
||||
if(segment_eq(get_fs(), KERNEL_DS)){
|
||||
memset((__force void*)mem, 0, len);
|
||||
return(0);
|
||||
}
|
||||
|
||||
return(access_ok(VERIFY_WRITE, mem, len) ?
|
||||
buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL) : len);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int strnlen_user_skas(const void __user *str, int len)
|
||||
{
|
||||
int count = 0, n;
|
||||
|
||||
if(segment_eq(get_fs(), KERNEL_DS))
|
||||
return(strnlen((__force char*)str, len) + 1);
|
||||
|
||||
n = buffer_op((unsigned long) str, len, 0, strnlen_chunk, &count);
|
||||
if(n == 0)
|
||||
return(count + 1);
|
||||
return(-EFAULT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
267
arch/um/kernel/smp.c
Normal file
267
arch/um/kernel/smp.c
Normal file
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/percpu.h"
|
||||
#include "asm/pgalloc.h"
|
||||
#include "asm/tlb.h"
|
||||
|
||||
/* For some reason, mmu_gathers are referenced when CONFIG_SMP is off. */
|
||||
DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
|
||||
#include "linux/sched.h"
|
||||
#include "linux/module.h"
|
||||
#include "linux/threads.h"
|
||||
#include "linux/interrupt.h"
|
||||
#include "linux/err.h"
|
||||
#include "linux/hardirq.h"
|
||||
#include "asm/smp.h"
|
||||
#include "asm/processor.h"
|
||||
#include "asm/spinlock.h"
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "kern.h"
|
||||
#include "irq_user.h"
|
||||
#include "os.h"
|
||||
|
||||
/* CPU online map, set by smp_boot_cpus */
|
||||
cpumask_t cpu_online_map = CPU_MASK_NONE;
|
||||
cpumask_t cpu_possible_map = CPU_MASK_NONE;
|
||||
|
||||
EXPORT_SYMBOL(cpu_online_map);
|
||||
EXPORT_SYMBOL(cpu_possible_map);
|
||||
|
||||
/* Per CPU bogomips and other parameters
|
||||
* The only piece used here is the ipi pipe, which is set before SMP is
|
||||
* started and never changed.
|
||||
*/
|
||||
struct cpuinfo_um cpu_data[NR_CPUS];
|
||||
|
||||
/* A statistic, can be a little off */
|
||||
int num_reschedules_sent = 0;
|
||||
|
||||
/* Not changed after boot */
|
||||
struct task_struct *idle_threads[NR_CPUS];
|
||||
|
||||
void smp_send_reschedule(int cpu)
|
||||
{
|
||||
os_write_file(cpu_data[cpu].ipi_pipe[1], "R", 1);
|
||||
num_reschedules_sent++;
|
||||
}
|
||||
|
||||
void smp_send_stop(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_INFO "Stopping all CPUs...");
|
||||
for(i = 0; i < num_online_cpus(); i++){
|
||||
if(i == current_thread->cpu)
|
||||
continue;
|
||||
os_write_file(cpu_data[i].ipi_pipe[1], "S", 1);
|
||||
}
|
||||
printk("done\n");
|
||||
}
|
||||
|
||||
static cpumask_t smp_commenced_mask = CPU_MASK_NONE;
|
||||
static cpumask_t cpu_callin_map = CPU_MASK_NONE;
|
||||
|
||||
static int idle_proc(void *cpup)
|
||||
{
|
||||
int cpu = (int) cpup, err;
|
||||
|
||||
err = os_pipe(cpu_data[cpu].ipi_pipe, 1, 1);
|
||||
if(err < 0)
|
||||
panic("CPU#%d failed to create IPI pipe, err = %d", cpu, -err);
|
||||
|
||||
os_set_fd_async(cpu_data[cpu].ipi_pipe[0],
|
||||
current->thread.mode.tt.extern_pid);
|
||||
|
||||
wmb();
|
||||
if (cpu_test_and_set(cpu, cpu_callin_map)) {
|
||||
printk("huh, CPU#%d already present??\n", cpu);
|
||||
BUG();
|
||||
}
|
||||
|
||||
while (!cpu_isset(cpu, smp_commenced_mask))
|
||||
cpu_relax();
|
||||
|
||||
cpu_set(cpu, cpu_online_map);
|
||||
default_idle();
|
||||
return(0);
|
||||
}
|
||||
|
||||
static struct task_struct *idle_thread(int cpu)
|
||||
{
|
||||
struct task_struct *new_task;
|
||||
unsigned char c;
|
||||
|
||||
current->thread.request.u.thread.proc = idle_proc;
|
||||
current->thread.request.u.thread.arg = (void *) cpu;
|
||||
new_task = fork_idle(cpu);
|
||||
if(IS_ERR(new_task))
|
||||
panic("copy_process failed in idle_thread, error = %ld",
|
||||
PTR_ERR(new_task));
|
||||
|
||||
cpu_tasks[cpu] = ((struct cpu_task)
|
||||
{ .pid = new_task->thread.mode.tt.extern_pid,
|
||||
.task = new_task } );
|
||||
idle_threads[cpu] = new_task;
|
||||
CHOOSE_MODE(os_write_file(new_task->thread.mode.tt.switch_pipe[1], &c,
|
||||
sizeof(c)),
|
||||
({ panic("skas mode doesn't support SMP"); }));
|
||||
return(new_task);
|
||||
}
|
||||
|
||||
void smp_prepare_cpus(unsigned int maxcpus)
|
||||
{
|
||||
struct task_struct *idle;
|
||||
unsigned long waittime;
|
||||
int err, cpu, me = smp_processor_id();
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ncpus; ++i)
|
||||
cpu_set(i, cpu_possible_map);
|
||||
|
||||
cpu_clear(me, cpu_online_map);
|
||||
cpu_set(me, cpu_online_map);
|
||||
cpu_set(me, cpu_callin_map);
|
||||
|
||||
err = os_pipe(cpu_data[me].ipi_pipe, 1, 1);
|
||||
if(err < 0)
|
||||
panic("CPU#0 failed to create IPI pipe, errno = %d", -err);
|
||||
|
||||
os_set_fd_async(cpu_data[me].ipi_pipe[0],
|
||||
current->thread.mode.tt.extern_pid);
|
||||
|
||||
for(cpu = 1; cpu < ncpus; cpu++){
|
||||
printk("Booting processor %d...\n", cpu);
|
||||
|
||||
idle = idle_thread(cpu);
|
||||
|
||||
init_idle(idle, cpu);
|
||||
|
||||
waittime = 200000000;
|
||||
while (waittime-- && !cpu_isset(cpu, cpu_callin_map))
|
||||
cpu_relax();
|
||||
|
||||
if (cpu_isset(cpu, cpu_callin_map))
|
||||
printk("done\n");
|
||||
else printk("failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
void smp_prepare_boot_cpu(void)
|
||||
{
|
||||
cpu_set(smp_processor_id(), cpu_online_map);
|
||||
}
|
||||
|
||||
int __cpu_up(unsigned int cpu)
|
||||
{
|
||||
cpu_set(cpu, smp_commenced_mask);
|
||||
while (!cpu_isset(cpu, cpu_online_map))
|
||||
mb();
|
||||
return(0);
|
||||
}
|
||||
|
||||
int setup_profiling_timer(unsigned int multiplier)
|
||||
{
|
||||
printk(KERN_INFO "setup_profiling_timer\n");
|
||||
return(0);
|
||||
}
|
||||
|
||||
void smp_call_function_slave(int cpu);
|
||||
|
||||
void IPI_handler(int cpu)
|
||||
{
|
||||
unsigned char c;
|
||||
int fd;
|
||||
|
||||
fd = cpu_data[cpu].ipi_pipe[0];
|
||||
while (os_read_file(fd, &c, 1) == 1) {
|
||||
switch (c) {
|
||||
case 'C':
|
||||
smp_call_function_slave(cpu);
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
set_tsk_need_resched(current);
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
printk("CPU#%d stopping\n", cpu);
|
||||
while(1)
|
||||
pause();
|
||||
break;
|
||||
|
||||
default:
|
||||
printk("CPU#%d received unknown IPI [%c]!\n", cpu, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int hard_smp_processor_id(void)
|
||||
{
|
||||
return(pid_to_processor_id(os_getpid()));
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(call_lock);
|
||||
static atomic_t scf_started;
|
||||
static atomic_t scf_finished;
|
||||
static void (*func)(void *info);
|
||||
static void *info;
|
||||
|
||||
void smp_call_function_slave(int cpu)
|
||||
{
|
||||
atomic_inc(&scf_started);
|
||||
(*func)(info);
|
||||
atomic_inc(&scf_finished);
|
||||
}
|
||||
|
||||
int smp_call_function(void (*_func)(void *info), void *_info, int nonatomic,
|
||||
int wait)
|
||||
{
|
||||
int cpus = num_online_cpus() - 1;
|
||||
int i;
|
||||
|
||||
if (!cpus)
|
||||
return 0;
|
||||
|
||||
/* Can deadlock when called with interrupts disabled */
|
||||
WARN_ON(irqs_disabled());
|
||||
|
||||
spin_lock_bh(&call_lock);
|
||||
atomic_set(&scf_started, 0);
|
||||
atomic_set(&scf_finished, 0);
|
||||
func = _func;
|
||||
info = _info;
|
||||
|
||||
for_each_online_cpu(i)
|
||||
os_write_file(cpu_data[i].ipi_pipe[1], "C", 1);
|
||||
|
||||
while (atomic_read(&scf_started) != cpus)
|
||||
barrier();
|
||||
|
||||
if (wait)
|
||||
while (atomic_read(&scf_finished) != cpus)
|
||||
barrier();
|
||||
|
||||
spin_unlock_bh(&call_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
163
arch/um/kernel/syscall.c
Normal file
163
arch/um/kernel/syscall.c
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/sched.h"
|
||||
#include "linux/file.h"
|
||||
#include "linux/smp_lock.h"
|
||||
#include "linux/mm.h"
|
||||
#include "linux/utsname.h"
|
||||
#include "linux/msg.h"
|
||||
#include "linux/shm.h"
|
||||
#include "linux/sys.h"
|
||||
#include "linux/syscalls.h"
|
||||
#include "linux/unistd.h"
|
||||
#include "linux/slab.h"
|
||||
#include "linux/utime.h"
|
||||
#include "asm/mman.h"
|
||||
#include "asm/uaccess.h"
|
||||
#include "kern_util.h"
|
||||
#include "user_util.h"
|
||||
#include "sysdep/syscalls.h"
|
||||
#include "mode_kern.h"
|
||||
#include "choose-mode.h"
|
||||
|
||||
/* Unlocked, I don't care if this is a bit off */
|
||||
int nsyscalls = 0;
|
||||
|
||||
long sys_fork(void)
|
||||
{
|
||||
long ret;
|
||||
|
||||
current->thread.forking = 1;
|
||||
ret = do_fork(SIGCHLD, UPT_SP(¤t->thread.regs.regs),
|
||||
¤t->thread.regs, 0, NULL, NULL);
|
||||
current->thread.forking = 0;
|
||||
return(ret);
|
||||
}
|
||||
|
||||
long sys_vfork(void)
|
||||
{
|
||||
long ret;
|
||||
|
||||
current->thread.forking = 1;
|
||||
ret = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD,
|
||||
UPT_SP(¤t->thread.regs.regs),
|
||||
¤t->thread.regs, 0, NULL, NULL);
|
||||
current->thread.forking = 0;
|
||||
return(ret);
|
||||
}
|
||||
|
||||
/* common code for old and new mmaps */
|
||||
long sys_mmap2(unsigned long addr, unsigned long len,
|
||||
unsigned long prot, unsigned long flags,
|
||||
unsigned long fd, unsigned long pgoff)
|
||||
{
|
||||
long error = -EBADF;
|
||||
struct file * file = NULL;
|
||||
|
||||
flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
|
||||
if (!(flags & MAP_ANONYMOUS)) {
|
||||
file = fget(fd);
|
||||
if (!file)
|
||||
goto out;
|
||||
}
|
||||
|
||||
down_write(¤t->mm->mmap_sem);
|
||||
error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
|
||||
up_write(¤t->mm->mmap_sem);
|
||||
|
||||
if (file)
|
||||
fput(file);
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
long old_mmap(unsigned long addr, unsigned long len,
|
||||
unsigned long prot, unsigned long flags,
|
||||
unsigned long fd, unsigned long offset)
|
||||
{
|
||||
long err = -EINVAL;
|
||||
if (offset & ~PAGE_MASK)
|
||||
goto out;
|
||||
|
||||
err = sys_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
/*
|
||||
* sys_pipe() is the normal C calling standard for creating
|
||||
* a pipe. It's not the way unix traditionally does this, though.
|
||||
*/
|
||||
long sys_pipe(unsigned long __user * fildes)
|
||||
{
|
||||
int fd[2];
|
||||
long error;
|
||||
|
||||
error = do_pipe(fd);
|
||||
if (!error) {
|
||||
if (copy_to_user(fildes, fd, sizeof(fd)))
|
||||
error = -EFAULT;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
long sys_uname(struct old_utsname __user * name)
|
||||
{
|
||||
long err;
|
||||
if (!name)
|
||||
return -EFAULT;
|
||||
down_read(&uts_sem);
|
||||
err = copy_to_user(name, utsname(), sizeof (*name));
|
||||
up_read(&uts_sem);
|
||||
return err?-EFAULT:0;
|
||||
}
|
||||
|
||||
long sys_olduname(struct oldold_utsname __user * name)
|
||||
{
|
||||
long error;
|
||||
|
||||
if (!name)
|
||||
return -EFAULT;
|
||||
if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname)))
|
||||
return -EFAULT;
|
||||
|
||||
down_read(&uts_sem);
|
||||
|
||||
error = __copy_to_user(&name->sysname, &utsname()->sysname,
|
||||
__OLD_UTS_LEN);
|
||||
error |= __put_user(0, name->sysname + __OLD_UTS_LEN);
|
||||
error |= __copy_to_user(&name->nodename, &utsname()->nodename,
|
||||
__OLD_UTS_LEN);
|
||||
error |= __put_user(0, name->nodename + __OLD_UTS_LEN);
|
||||
error |= __copy_to_user(&name->release, &utsname()->release,
|
||||
__OLD_UTS_LEN);
|
||||
error |= __put_user(0, name->release + __OLD_UTS_LEN);
|
||||
error |= __copy_to_user(&name->version, &utsname()->version,
|
||||
__OLD_UTS_LEN);
|
||||
error |= __put_user(0, name->version + __OLD_UTS_LEN);
|
||||
error |= __copy_to_user(&name->machine, &utsname()->machine,
|
||||
__OLD_UTS_LEN);
|
||||
error |= __put_user(0, name->machine + __OLD_UTS_LEN);
|
||||
|
||||
up_read(&uts_sem);
|
||||
|
||||
error = error ? -EFAULT : 0;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int kernel_execve(const char *filename, char *const argv[], char *const envp[])
|
||||
{
|
||||
mm_segment_t fs;
|
||||
int ret;
|
||||
|
||||
fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
ret = um_execve(filename, argv, envp);
|
||||
set_fs(fs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
81
arch/um/kernel/sysrq.c
Normal file
81
arch/um/kernel/sysrq.c
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/sched.h"
|
||||
#include "linux/kernel.h"
|
||||
#include "linux/module.h"
|
||||
#include "linux/kallsyms.h"
|
||||
#include "asm/page.h"
|
||||
#include "asm/processor.h"
|
||||
#include "sysrq.h"
|
||||
#include "user_util.h"
|
||||
|
||||
/* Catch non-i386 SUBARCH's. */
|
||||
#if !defined(CONFIG_UML_X86) || defined(CONFIG_64BIT)
|
||||
void show_trace(struct task_struct *task, unsigned long * stack)
|
||||
{
|
||||
unsigned long addr;
|
||||
|
||||
if (!stack) {
|
||||
stack = (unsigned long*) &stack;
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
printk("Call Trace: \n");
|
||||
while (((long) stack & (THREAD_SIZE-1)) != 0) {
|
||||
addr = *stack;
|
||||
if (__kernel_text_address(addr)) {
|
||||
printk("%08lx: [<%08lx>]", (unsigned long) stack, addr);
|
||||
print_symbol(" %s", addr);
|
||||
printk("\n");
|
||||
}
|
||||
stack++;
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* stack dumps generator - this is used by arch-independent code.
|
||||
* And this is identical to i386 currently.
|
||||
*/
|
||||
void dump_stack(void)
|
||||
{
|
||||
unsigned long stack;
|
||||
|
||||
show_trace(current, &stack);
|
||||
}
|
||||
EXPORT_SYMBOL(dump_stack);
|
||||
|
||||
/*Stolen from arch/i386/kernel/traps.c */
|
||||
static const int kstack_depth_to_print = 24;
|
||||
|
||||
/* This recently started being used in arch-independent code too, as in
|
||||
* kernel/sched.c.*/
|
||||
void show_stack(struct task_struct *task, unsigned long *esp)
|
||||
{
|
||||
unsigned long *stack;
|
||||
int i;
|
||||
|
||||
if (esp == NULL) {
|
||||
if (task != current && task != NULL) {
|
||||
esp = (unsigned long *) KSTK_ESP(task);
|
||||
} else {
|
||||
esp = (unsigned long *) &esp;
|
||||
}
|
||||
}
|
||||
|
||||
stack = esp;
|
||||
for(i = 0; i < kstack_depth_to_print; i++) {
|
||||
if (kstack_end(stack))
|
||||
break;
|
||||
if (i && ((i % 8) == 0))
|
||||
printk("\n ");
|
||||
printk("%08lx ", *stack++);
|
||||
}
|
||||
|
||||
printk("Call Trace: \n");
|
||||
show_trace(task, esp);
|
||||
}
|
||||
180
arch/um/kernel/time.c
Normal file
180
arch/um/kernel/time.c
Normal file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/kernel.h"
|
||||
#include "linux/module.h"
|
||||
#include "linux/unistd.h"
|
||||
#include "linux/stddef.h"
|
||||
#include "linux/spinlock.h"
|
||||
#include "linux/time.h"
|
||||
#include "linux/sched.h"
|
||||
#include "linux/interrupt.h"
|
||||
#include "linux/init.h"
|
||||
#include "linux/delay.h"
|
||||
#include "linux/hrtimer.h"
|
||||
#include "asm/irq.h"
|
||||
#include "asm/param.h"
|
||||
#include "asm/current.h"
|
||||
#include "kern_util.h"
|
||||
#include "user_util.h"
|
||||
#include "mode.h"
|
||||
#include "os.h"
|
||||
|
||||
int hz(void)
|
||||
{
|
||||
return(HZ);
|
||||
}
|
||||
|
||||
/*
|
||||
* Scheduler clock - returns current time in nanosec units.
|
||||
*/
|
||||
unsigned long long sched_clock(void)
|
||||
{
|
||||
return (unsigned long long)jiffies_64 * (1000000000 / HZ);
|
||||
}
|
||||
|
||||
static unsigned long long prev_nsecs[NR_CPUS];
|
||||
#ifdef CONFIG_UML_REAL_TIME_CLOCK
|
||||
static long long delta[NR_CPUS]; /* Deviation per interval */
|
||||
#endif
|
||||
|
||||
void timer_irq(union uml_pt_regs *regs)
|
||||
{
|
||||
unsigned long long ticks = 0;
|
||||
#ifdef CONFIG_UML_REAL_TIME_CLOCK
|
||||
int c = cpu();
|
||||
if(prev_nsecs[c]){
|
||||
/* We've had 1 tick */
|
||||
unsigned long long nsecs = os_nsecs();
|
||||
|
||||
delta[c] += nsecs - prev_nsecs[c];
|
||||
prev_nsecs[c] = nsecs;
|
||||
|
||||
/* Protect against the host clock being set backwards */
|
||||
if(delta[c] < 0)
|
||||
delta[c] = 0;
|
||||
|
||||
ticks += (delta[c] * HZ) / BILLION;
|
||||
delta[c] -= (ticks * BILLION) / HZ;
|
||||
}
|
||||
else prev_nsecs[c] = os_nsecs();
|
||||
#else
|
||||
ticks = 1;
|
||||
#endif
|
||||
while(ticks > 0){
|
||||
do_IRQ(TIMER_IRQ, regs);
|
||||
ticks--;
|
||||
}
|
||||
}
|
||||
|
||||
/* Protects local_offset */
|
||||
static DEFINE_SPINLOCK(timer_spinlock);
|
||||
static unsigned long long local_offset = 0;
|
||||
|
||||
static inline unsigned long long get_time(void)
|
||||
{
|
||||
unsigned long long nsecs;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&timer_spinlock, flags);
|
||||
nsecs = os_nsecs();
|
||||
nsecs += local_offset;
|
||||
spin_unlock_irqrestore(&timer_spinlock, flags);
|
||||
|
||||
return nsecs;
|
||||
}
|
||||
|
||||
irqreturn_t um_timer(int irq, void *dev)
|
||||
{
|
||||
unsigned long long nsecs;
|
||||
unsigned long flags;
|
||||
|
||||
write_seqlock_irqsave(&xtime_lock, flags);
|
||||
|
||||
do_timer(1);
|
||||
|
||||
nsecs = get_time();
|
||||
xtime.tv_sec = nsecs / NSEC_PER_SEC;
|
||||
xtime.tv_nsec = nsecs - xtime.tv_sec * NSEC_PER_SEC;
|
||||
|
||||
write_sequnlock_irqrestore(&xtime_lock, flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void register_timer(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = request_irq(TIMER_IRQ, um_timer, IRQF_DISABLED, "timer", NULL);
|
||||
if(err != 0)
|
||||
printk(KERN_ERR "register_timer : request_irq failed - "
|
||||
"errno = %d\n", -err);
|
||||
|
||||
err = set_interval(1);
|
||||
if(err != 0)
|
||||
printk(KERN_ERR "register_timer : set_interval failed - "
|
||||
"errno = %d\n", -err);
|
||||
}
|
||||
|
||||
extern void (*late_time_init)(void);
|
||||
|
||||
void time_init(void)
|
||||
{
|
||||
long long nsecs;
|
||||
|
||||
nsecs = os_nsecs();
|
||||
set_normalized_timespec(&wall_to_monotonic, -nsecs / BILLION,
|
||||
-nsecs % BILLION);
|
||||
late_time_init = register_timer;
|
||||
}
|
||||
|
||||
void do_gettimeofday(struct timeval *tv)
|
||||
{
|
||||
unsigned long long nsecs = get_time();
|
||||
|
||||
tv->tv_sec = nsecs / NSEC_PER_SEC;
|
||||
/* Careful about calculations here - this was originally done as
|
||||
* (nsecs - tv->tv_sec * NSEC_PER_SEC) / NSEC_PER_USEC
|
||||
* which gave bogus (> 1000000) values. Dunno why, suspect gcc
|
||||
* (4.0.0) miscompiled it, or there's a subtle 64/32-bit conversion
|
||||
* problem that I missed.
|
||||
*/
|
||||
nsecs -= tv->tv_sec * NSEC_PER_SEC;
|
||||
tv->tv_usec = (unsigned long) nsecs / NSEC_PER_USEC;
|
||||
}
|
||||
|
||||
static inline void set_time(unsigned long long nsecs)
|
||||
{
|
||||
unsigned long long now;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&timer_spinlock, flags);
|
||||
now = os_nsecs();
|
||||
local_offset = nsecs - now;
|
||||
spin_unlock_irqrestore(&timer_spinlock, flags);
|
||||
|
||||
clock_was_set();
|
||||
}
|
||||
|
||||
int do_settimeofday(struct timespec *tv)
|
||||
{
|
||||
set_time((unsigned long long) tv->tv_sec * NSEC_PER_SEC + tv->tv_nsec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void timer_handler(int sig, union uml_pt_regs *regs)
|
||||
{
|
||||
local_irq_disable();
|
||||
irq_enter();
|
||||
update_process_times(CHOOSE_MODE(
|
||||
(UPT_SC(regs) && user_context(UPT_SP(regs))),
|
||||
(regs)->skas.is_user));
|
||||
irq_exit();
|
||||
local_irq_enable();
|
||||
if(current_thread->cpu == 0)
|
||||
timer_irq(regs);
|
||||
}
|
||||
390
arch/um/kernel/tlb.c
Normal file
390
arch/um/kernel/tlb.c
Normal file
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/mm.h"
|
||||
#include "asm/page.h"
|
||||
#include "asm/pgalloc.h"
|
||||
#include "asm/tlbflush.h"
|
||||
#include "choose-mode.h"
|
||||
#include "mode_kern.h"
|
||||
#include "user_util.h"
|
||||
#include "tlb.h"
|
||||
#include "mem.h"
|
||||
#include "mem_user.h"
|
||||
#include "os.h"
|
||||
|
||||
static int add_mmap(unsigned long virt, unsigned long phys, unsigned long len,
|
||||
int r, int w, int x, struct host_vm_op *ops, int *index,
|
||||
int last_filled, union mm_context *mmu, void **flush,
|
||||
int (*do_ops)(union mm_context *, struct host_vm_op *,
|
||||
int, int, void **))
|
||||
{
|
||||
__u64 offset;
|
||||
struct host_vm_op *last;
|
||||
int fd, ret = 0;
|
||||
|
||||
fd = phys_mapping(phys, &offset);
|
||||
if(*index != -1){
|
||||
last = &ops[*index];
|
||||
if((last->type == MMAP) &&
|
||||
(last->u.mmap.addr + last->u.mmap.len == virt) &&
|
||||
(last->u.mmap.r == r) && (last->u.mmap.w == w) &&
|
||||
(last->u.mmap.x == x) && (last->u.mmap.fd == fd) &&
|
||||
(last->u.mmap.offset + last->u.mmap.len == offset)){
|
||||
last->u.mmap.len += len;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(*index == last_filled){
|
||||
ret = (*do_ops)(mmu, ops, last_filled, 0, flush);
|
||||
*index = -1;
|
||||
}
|
||||
|
||||
ops[++*index] = ((struct host_vm_op) { .type = MMAP,
|
||||
.u = { .mmap = {
|
||||
.addr = virt,
|
||||
.len = len,
|
||||
.r = r,
|
||||
.w = w,
|
||||
.x = x,
|
||||
.fd = fd,
|
||||
.offset = offset }
|
||||
} });
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int add_munmap(unsigned long addr, unsigned long len,
|
||||
struct host_vm_op *ops, int *index, int last_filled,
|
||||
union mm_context *mmu, void **flush,
|
||||
int (*do_ops)(union mm_context *, struct host_vm_op *,
|
||||
int, int, void **))
|
||||
{
|
||||
struct host_vm_op *last;
|
||||
int ret = 0;
|
||||
|
||||
if(*index != -1){
|
||||
last = &ops[*index];
|
||||
if((last->type == MUNMAP) &&
|
||||
(last->u.munmap.addr + last->u.mmap.len == addr)){
|
||||
last->u.munmap.len += len;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(*index == last_filled){
|
||||
ret = (*do_ops)(mmu, ops, last_filled, 0, flush);
|
||||
*index = -1;
|
||||
}
|
||||
|
||||
ops[++*index] = ((struct host_vm_op) { .type = MUNMAP,
|
||||
.u = { .munmap = {
|
||||
.addr = addr,
|
||||
.len = len } } });
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int add_mprotect(unsigned long addr, unsigned long len, int r, int w,
|
||||
int x, struct host_vm_op *ops, int *index,
|
||||
int last_filled, union mm_context *mmu, void **flush,
|
||||
int (*do_ops)(union mm_context *, struct host_vm_op *,
|
||||
int, int, void **))
|
||||
{
|
||||
struct host_vm_op *last;
|
||||
int ret = 0;
|
||||
|
||||
if(*index != -1){
|
||||
last = &ops[*index];
|
||||
if((last->type == MPROTECT) &&
|
||||
(last->u.mprotect.addr + last->u.mprotect.len == addr) &&
|
||||
(last->u.mprotect.r == r) && (last->u.mprotect.w == w) &&
|
||||
(last->u.mprotect.x == x)){
|
||||
last->u.mprotect.len += len;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(*index == last_filled){
|
||||
ret = (*do_ops)(mmu, ops, last_filled, 0, flush);
|
||||
*index = -1;
|
||||
}
|
||||
|
||||
ops[++*index] = ((struct host_vm_op) { .type = MPROTECT,
|
||||
.u = { .mprotect = {
|
||||
.addr = addr,
|
||||
.len = len,
|
||||
.r = r,
|
||||
.w = w,
|
||||
.x = x } } });
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define ADD_ROUND(n, inc) (((n) + (inc)) & ~((inc) - 1))
|
||||
|
||||
void fix_range_common(struct mm_struct *mm, unsigned long start_addr,
|
||||
unsigned long end_addr, int force,
|
||||
int (*do_ops)(union mm_context *, struct host_vm_op *,
|
||||
int, int, void **))
|
||||
{
|
||||
pgd_t *npgd;
|
||||
pud_t *npud;
|
||||
pmd_t *npmd;
|
||||
pte_t *npte;
|
||||
union mm_context *mmu = &mm->context;
|
||||
unsigned long addr, end;
|
||||
int r, w, x;
|
||||
struct host_vm_op ops[1];
|
||||
void *flush = NULL;
|
||||
int op_index = -1, last_op = ARRAY_SIZE(ops) - 1;
|
||||
int ret = 0;
|
||||
|
||||
if(mm == NULL)
|
||||
return;
|
||||
|
||||
ops[0].type = NONE;
|
||||
for(addr = start_addr; addr < end_addr && !ret;){
|
||||
npgd = pgd_offset(mm, addr);
|
||||
if(!pgd_present(*npgd)){
|
||||
end = ADD_ROUND(addr, PGDIR_SIZE);
|
||||
if(end > end_addr)
|
||||
end = end_addr;
|
||||
if(force || pgd_newpage(*npgd)){
|
||||
ret = add_munmap(addr, end - addr, ops,
|
||||
&op_index, last_op, mmu,
|
||||
&flush, do_ops);
|
||||
pgd_mkuptodate(*npgd);
|
||||
}
|
||||
addr = end;
|
||||
continue;
|
||||
}
|
||||
|
||||
npud = pud_offset(npgd, addr);
|
||||
if(!pud_present(*npud)){
|
||||
end = ADD_ROUND(addr, PUD_SIZE);
|
||||
if(end > end_addr)
|
||||
end = end_addr;
|
||||
if(force || pud_newpage(*npud)){
|
||||
ret = add_munmap(addr, end - addr, ops,
|
||||
&op_index, last_op, mmu,
|
||||
&flush, do_ops);
|
||||
pud_mkuptodate(*npud);
|
||||
}
|
||||
addr = end;
|
||||
continue;
|
||||
}
|
||||
|
||||
npmd = pmd_offset(npud, addr);
|
||||
if(!pmd_present(*npmd)){
|
||||
end = ADD_ROUND(addr, PMD_SIZE);
|
||||
if(end > end_addr)
|
||||
end = end_addr;
|
||||
if(force || pmd_newpage(*npmd)){
|
||||
ret = add_munmap(addr, end - addr, ops,
|
||||
&op_index, last_op, mmu,
|
||||
&flush, do_ops);
|
||||
pmd_mkuptodate(*npmd);
|
||||
}
|
||||
addr = end;
|
||||
continue;
|
||||
}
|
||||
|
||||
npte = pte_offset_kernel(npmd, addr);
|
||||
r = pte_read(*npte);
|
||||
w = pte_write(*npte);
|
||||
x = pte_exec(*npte);
|
||||
if (!pte_young(*npte)) {
|
||||
r = 0;
|
||||
w = 0;
|
||||
} else if (!pte_dirty(*npte)) {
|
||||
w = 0;
|
||||
}
|
||||
if(force || pte_newpage(*npte)){
|
||||
if(pte_present(*npte))
|
||||
ret = add_mmap(addr,
|
||||
pte_val(*npte) & PAGE_MASK,
|
||||
PAGE_SIZE, r, w, x, ops,
|
||||
&op_index, last_op, mmu,
|
||||
&flush, do_ops);
|
||||
else ret = add_munmap(addr, PAGE_SIZE, ops,
|
||||
&op_index, last_op, mmu,
|
||||
&flush, do_ops);
|
||||
}
|
||||
else if(pte_newprot(*npte))
|
||||
ret = add_mprotect(addr, PAGE_SIZE, r, w, x, ops,
|
||||
&op_index, last_op, mmu,
|
||||
&flush, do_ops);
|
||||
|
||||
*npte = pte_mkuptodate(*npte);
|
||||
addr += PAGE_SIZE;
|
||||
}
|
||||
if(!ret)
|
||||
ret = (*do_ops)(mmu, ops, op_index, 1, &flush);
|
||||
|
||||
/* This is not an else because ret is modified above */
|
||||
if(ret) {
|
||||
printk("fix_range_common: failed, killing current process\n");
|
||||
force_sig(SIGKILL, current);
|
||||
}
|
||||
}
|
||||
|
||||
int flush_tlb_kernel_range_common(unsigned long start, unsigned long end)
|
||||
{
|
||||
struct mm_struct *mm;
|
||||
pgd_t *pgd;
|
||||
pud_t *pud;
|
||||
pmd_t *pmd;
|
||||
pte_t *pte;
|
||||
unsigned long addr, last;
|
||||
int updated = 0, err;
|
||||
|
||||
mm = &init_mm;
|
||||
for(addr = start; addr < end;){
|
||||
pgd = pgd_offset(mm, addr);
|
||||
if(!pgd_present(*pgd)){
|
||||
last = ADD_ROUND(addr, PGDIR_SIZE);
|
||||
if(last > end)
|
||||
last = end;
|
||||
if(pgd_newpage(*pgd)){
|
||||
updated = 1;
|
||||
err = os_unmap_memory((void *) addr,
|
||||
last - addr);
|
||||
if(err < 0)
|
||||
panic("munmap failed, errno = %d\n",
|
||||
-err);
|
||||
}
|
||||
addr = last;
|
||||
continue;
|
||||
}
|
||||
|
||||
pud = pud_offset(pgd, addr);
|
||||
if(!pud_present(*pud)){
|
||||
last = ADD_ROUND(addr, PUD_SIZE);
|
||||
if(last > end)
|
||||
last = end;
|
||||
if(pud_newpage(*pud)){
|
||||
updated = 1;
|
||||
err = os_unmap_memory((void *) addr,
|
||||
last - addr);
|
||||
if(err < 0)
|
||||
panic("munmap failed, errno = %d\n",
|
||||
-err);
|
||||
}
|
||||
addr = last;
|
||||
continue;
|
||||
}
|
||||
|
||||
pmd = pmd_offset(pud, addr);
|
||||
if(!pmd_present(*pmd)){
|
||||
last = ADD_ROUND(addr, PMD_SIZE);
|
||||
if(last > end)
|
||||
last = end;
|
||||
if(pmd_newpage(*pmd)){
|
||||
updated = 1;
|
||||
err = os_unmap_memory((void *) addr,
|
||||
last - addr);
|
||||
if(err < 0)
|
||||
panic("munmap failed, errno = %d\n",
|
||||
-err);
|
||||
}
|
||||
addr = last;
|
||||
continue;
|
||||
}
|
||||
|
||||
pte = pte_offset_kernel(pmd, addr);
|
||||
if(!pte_present(*pte) || pte_newpage(*pte)){
|
||||
updated = 1;
|
||||
err = os_unmap_memory((void *) addr,
|
||||
PAGE_SIZE);
|
||||
if(err < 0)
|
||||
panic("munmap failed, errno = %d\n",
|
||||
-err);
|
||||
if(pte_present(*pte))
|
||||
map_memory(addr,
|
||||
pte_val(*pte) & PAGE_MASK,
|
||||
PAGE_SIZE, 1, 1, 1);
|
||||
}
|
||||
else if(pte_newprot(*pte)){
|
||||
updated = 1;
|
||||
os_protect_memory((void *) addr, PAGE_SIZE, 1, 1, 1);
|
||||
}
|
||||
addr += PAGE_SIZE;
|
||||
}
|
||||
return(updated);
|
||||
}
|
||||
|
||||
pgd_t *pgd_offset_proc(struct mm_struct *mm, unsigned long address)
|
||||
{
|
||||
return(pgd_offset(mm, address));
|
||||
}
|
||||
|
||||
pud_t *pud_offset_proc(pgd_t *pgd, unsigned long address)
|
||||
{
|
||||
return(pud_offset(pgd, address));
|
||||
}
|
||||
|
||||
pmd_t *pmd_offset_proc(pud_t *pud, unsigned long address)
|
||||
{
|
||||
return(pmd_offset(pud, address));
|
||||
}
|
||||
|
||||
pte_t *pte_offset_proc(pmd_t *pmd, unsigned long address)
|
||||
{
|
||||
return(pte_offset_kernel(pmd, address));
|
||||
}
|
||||
|
||||
pte_t *addr_pte(struct task_struct *task, unsigned long addr)
|
||||
{
|
||||
pgd_t *pgd = pgd_offset(task->mm, addr);
|
||||
pud_t *pud = pud_offset(pgd, addr);
|
||||
pmd_t *pmd = pmd_offset(pud, addr);
|
||||
|
||||
return(pte_offset_map(pmd, addr));
|
||||
}
|
||||
|
||||
void flush_tlb_page(struct vm_area_struct *vma, unsigned long address)
|
||||
{
|
||||
address &= PAGE_MASK;
|
||||
flush_tlb_range(vma, address, address + PAGE_SIZE);
|
||||
}
|
||||
|
||||
void flush_tlb_all(void)
|
||||
{
|
||||
flush_tlb_mm(current->mm);
|
||||
}
|
||||
|
||||
void flush_tlb_kernel_range(unsigned long start, unsigned long end)
|
||||
{
|
||||
CHOOSE_MODE_PROC(flush_tlb_kernel_range_tt,
|
||||
flush_tlb_kernel_range_common, start, end);
|
||||
}
|
||||
|
||||
void flush_tlb_kernel_vm(void)
|
||||
{
|
||||
CHOOSE_MODE(flush_tlb_kernel_vm_tt(),
|
||||
flush_tlb_kernel_range_common(start_vm, end_vm));
|
||||
}
|
||||
|
||||
void __flush_tlb_one(unsigned long addr)
|
||||
{
|
||||
CHOOSE_MODE_PROC(__flush_tlb_one_tt, __flush_tlb_one_skas, addr);
|
||||
}
|
||||
|
||||
void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
|
||||
unsigned long end)
|
||||
{
|
||||
CHOOSE_MODE_PROC(flush_tlb_range_tt, flush_tlb_range_skas, vma, start,
|
||||
end);
|
||||
}
|
||||
|
||||
void flush_tlb_mm(struct mm_struct *mm)
|
||||
{
|
||||
CHOOSE_MODE_PROC(flush_tlb_mm_tt, flush_tlb_mm_skas, mm);
|
||||
}
|
||||
|
||||
void force_flush_all(void)
|
||||
{
|
||||
CHOOSE_MODE(force_flush_all_tt(), force_flush_all_skas());
|
||||
}
|
||||
|
||||
258
arch/um/kernel/trap.c
Normal file
258
arch/um/kernel/trap.c
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/kernel.h"
|
||||
#include "asm/errno.h"
|
||||
#include "linux/sched.h"
|
||||
#include "linux/mm.h"
|
||||
#include "linux/spinlock.h"
|
||||
#include "linux/init.h"
|
||||
#include "linux/ptrace.h"
|
||||
#include "asm/semaphore.h"
|
||||
#include "asm/pgtable.h"
|
||||
#include "asm/pgalloc.h"
|
||||
#include "asm/tlbflush.h"
|
||||
#include "asm/a.out.h"
|
||||
#include "asm/current.h"
|
||||
#include "asm/irq.h"
|
||||
#include "sysdep/sigcontext.h"
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "kern.h"
|
||||
#include "chan_kern.h"
|
||||
#include "mconsole_kern.h"
|
||||
#include "mem.h"
|
||||
#include "mem_kern.h"
|
||||
#include "sysdep/sigcontext.h"
|
||||
#include "sysdep/ptrace.h"
|
||||
#include "os.h"
|
||||
#ifdef CONFIG_MODE_SKAS
|
||||
#include "skas.h"
|
||||
#endif
|
||||
#include "os.h"
|
||||
|
||||
/* Note this is constrained to return 0, -EFAULT, -EACCESS, -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;
|
||||
pgd_t *pgd;
|
||||
pud_t *pud;
|
||||
pmd_t *pmd;
|
||||
pte_t *pte;
|
||||
int err = -EFAULT;
|
||||
|
||||
*code_out = SEGV_MAPERR;
|
||||
|
||||
/* If the fault was during atomic operation, don't take the fault, just
|
||||
* fail. */
|
||||
if (in_atomic())
|
||||
goto out_nosemaphore;
|
||||
|
||||
down_read(&mm->mmap_sem);
|
||||
vma = find_vma(mm, address);
|
||||
if(!vma)
|
||||
goto out;
|
||||
else if(vma->vm_start <= address)
|
||||
goto good_area;
|
||||
else if(!(vma->vm_flags & VM_GROWSDOWN))
|
||||
goto out;
|
||||
else if(is_user && !ARCH_IS_STACKGROW(address))
|
||||
goto out;
|
||||
else if(expand_stack(vma, address))
|
||||
goto out;
|
||||
|
||||
good_area:
|
||||
*code_out = SEGV_ACCERR;
|
||||
if(is_write && !(vma->vm_flags & VM_WRITE))
|
||||
goto out;
|
||||
|
||||
/* Don't require VM_READ|VM_EXEC for write faults! */
|
||||
if(!is_write && !(vma->vm_flags & (VM_READ | VM_EXEC)))
|
||||
goto out;
|
||||
|
||||
do {
|
||||
survive:
|
||||
switch (handle_mm_fault(mm, vma, address, is_write)){
|
||||
case VM_FAULT_MINOR:
|
||||
current->min_flt++;
|
||||
break;
|
||||
case VM_FAULT_MAJOR:
|
||||
current->maj_flt++;
|
||||
break;
|
||||
case VM_FAULT_SIGBUS:
|
||||
err = -EACCES;
|
||||
goto out;
|
||||
case VM_FAULT_OOM:
|
||||
err = -ENOMEM;
|
||||
goto out_of_memory;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
pgd = pgd_offset(mm, address);
|
||||
pud = pud_offset(pgd, address);
|
||||
pmd = pmd_offset(pud, 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
|
||||
flush_tlb_page(vma, address);
|
||||
out:
|
||||
up_read(&mm->mmap_sem);
|
||||
out_nosemaphore:
|
||||
return(err);
|
||||
|
||||
/*
|
||||
* We ran out of memory, or some other thing happened to us that made
|
||||
* us unable to handle the page fault gracefully.
|
||||
*/
|
||||
out_of_memory:
|
||||
if (is_init(current)) {
|
||||
up_read(&mm->mmap_sem);
|
||||
yield();
|
||||
down_read(&mm->mmap_sem);
|
||||
goto survive;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void bad_segv(struct faultinfo fi, unsigned long ip)
|
||||
{
|
||||
struct siginfo si;
|
||||
|
||||
si.si_signo = SIGSEGV;
|
||||
si.si_code = SEGV_ACCERR;
|
||||
si.si_addr = (void __user *) FAULT_ADDRESS(fi);
|
||||
current->thread.arch.faultinfo = fi;
|
||||
force_sig_info(SIGSEGV, &si, current);
|
||||
}
|
||||
|
||||
static void segv_handler(int sig, union uml_pt_regs *regs)
|
||||
{
|
||||
struct faultinfo * fi = UPT_FAULTINFO(regs);
|
||||
|
||||
if(UPT_IS_USER(regs) && !SEGV_IS_FIXABLE(fi)){
|
||||
bad_segv(*fi, UPT_IP(regs));
|
||||
return;
|
||||
}
|
||||
segv(*fi, UPT_IP(regs), UPT_IS_USER(regs), regs);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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, void *sc)
|
||||
{
|
||||
struct siginfo si;
|
||||
void *catcher;
|
||||
int err;
|
||||
int is_write = FAULT_WRITE(fi);
|
||||
unsigned long address = FAULT_ADDRESS(fi);
|
||||
|
||||
if(!is_user && (address >= start_vm) && (address < end_vm)){
|
||||
flush_tlb_kernel_vm();
|
||||
return(0);
|
||||
}
|
||||
else if(current->mm == NULL)
|
||||
panic("Segfault with no mm");
|
||||
|
||||
if (SEGV_IS_FIXABLE(&fi) || SEGV_MAYBE_FIXABLE(&fi))
|
||||
err = handle_page_fault(address, ip, is_write, is_user, &si.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. */
|
||||
address = 0;
|
||||
}
|
||||
|
||||
catcher = current->thread.fault_catcher;
|
||||
if(!err)
|
||||
return(0);
|
||||
else if(catcher != NULL){
|
||||
current->thread.fault_addr = (void *) address;
|
||||
do_longjmp(catcher, 1);
|
||||
}
|
||||
else if(current->thread.fault_addr != NULL)
|
||||
panic("fault_addr set but no fault catcher");
|
||||
else if(!is_user && arch_fixup(ip, sc))
|
||||
return(0);
|
||||
|
||||
if(!is_user)
|
||||
panic("Kernel mode fault at addr 0x%lx, ip 0x%lx",
|
||||
address, ip);
|
||||
|
||||
if (err == -EACCES) {
|
||||
si.si_signo = SIGBUS;
|
||||
si.si_errno = 0;
|
||||
si.si_code = BUS_ADRERR;
|
||||
si.si_addr = (void __user *)address;
|
||||
current->thread.arch.faultinfo = fi;
|
||||
force_sig_info(SIGBUS, &si, current);
|
||||
} else if (err == -ENOMEM) {
|
||||
printk("VM: killing process %s\n", current->comm);
|
||||
do_exit(SIGKILL);
|
||||
} else {
|
||||
BUG_ON(err != -EFAULT);
|
||||
si.si_signo = SIGSEGV;
|
||||
si.si_addr = (void __user *) address;
|
||||
current->thread.arch.faultinfo = fi;
|
||||
force_sig_info(SIGSEGV, &si, current);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
void relay_signal(int sig, union uml_pt_regs *regs)
|
||||
{
|
||||
if(arch_handle_signal(sig, regs))
|
||||
return;
|
||||
|
||||
if(!UPT_IS_USER(regs)){
|
||||
if(sig == SIGBUS)
|
||||
printk("Bus error - the /dev/shm or /tmp mount likely "
|
||||
"just ran out of space\n");
|
||||
panic("Kernel mode signal %d", sig);
|
||||
}
|
||||
|
||||
current->thread.arch.faultinfo = *UPT_FAULTINFO(regs);
|
||||
force_sig(sig, current);
|
||||
}
|
||||
|
||||
static void bus_handler(int sig, union uml_pt_regs *regs)
|
||||
{
|
||||
if(current->thread.fault_catcher != NULL)
|
||||
do_longjmp(current->thread.fault_catcher, 1);
|
||||
else relay_signal(sig, regs);
|
||||
}
|
||||
|
||||
static void winch(int sig, union uml_pt_regs *regs)
|
||||
{
|
||||
do_IRQ(WINCH_IRQ, regs);
|
||||
}
|
||||
|
||||
const struct kern_handlers handlinfo_kern = {
|
||||
.relay_signal = relay_signal,
|
||||
.winch = winch,
|
||||
.bus_handler = bus_handler,
|
||||
.page_fault = segv_handler,
|
||||
.sigio_handler = sigio_handler,
|
||||
.timer_handler = timer_handler
|
||||
};
|
||||
|
||||
void trap_init(void)
|
||||
{
|
||||
}
|
||||
14
arch/um/kernel/tt/Makefile
Normal file
14
arch/um/kernel/tt/Makefile
Normal file
@@ -0,0 +1,14 @@
|
||||
#
|
||||
# Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com)
|
||||
# Licensed under the GPL
|
||||
#
|
||||
|
||||
obj-y = exec_kern.o exec_user.o gdb.o ksyms.o mem.o mem_user.o process_kern.o \
|
||||
syscall_kern.o syscall_user.o tlb.o tracer.o trap_user.o \
|
||||
uaccess.o uaccess_user.o
|
||||
|
||||
obj-$(CONFIG_PT_PROXY) += gdb_kern.o ptproxy/
|
||||
|
||||
USER_OBJS := gdb.o tracer.o
|
||||
|
||||
include arch/um/scripts/Makefile.rules
|
||||
85
arch/um/kernel/tt/exec_kern.c
Normal file
85
arch/um/kernel/tt/exec_kern.c
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/kernel.h"
|
||||
#include "linux/mm.h"
|
||||
#include "asm/signal.h"
|
||||
#include "asm/ptrace.h"
|
||||
#include "asm/uaccess.h"
|
||||
#include "asm/pgalloc.h"
|
||||
#include "asm/tlbflush.h"
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "irq_user.h"
|
||||
#include "mem_user.h"
|
||||
#include "os.h"
|
||||
#include "tlb.h"
|
||||
#include "mode.h"
|
||||
|
||||
static int exec_tramp(void *sig_stack)
|
||||
{
|
||||
init_new_thread_stack(sig_stack, NULL);
|
||||
init_new_thread_signals();
|
||||
os_stop_process(os_getpid());
|
||||
return(0);
|
||||
}
|
||||
|
||||
void flush_thread_tt(void)
|
||||
{
|
||||
unsigned long stack;
|
||||
int new_pid;
|
||||
|
||||
stack = alloc_stack(0, 0);
|
||||
if(stack == 0){
|
||||
printk(KERN_ERR
|
||||
"flush_thread : failed to allocate temporary stack\n");
|
||||
do_exit(SIGKILL);
|
||||
}
|
||||
|
||||
new_pid = start_fork_tramp(task_stack_page(current), stack, 0, exec_tramp);
|
||||
if(new_pid < 0){
|
||||
printk(KERN_ERR
|
||||
"flush_thread : new thread failed, errno = %d\n",
|
||||
-new_pid);
|
||||
do_exit(SIGKILL);
|
||||
}
|
||||
|
||||
if(current_thread->cpu == 0)
|
||||
forward_interrupts(new_pid);
|
||||
current->thread.request.op = OP_EXEC;
|
||||
current->thread.request.u.exec.pid = new_pid;
|
||||
unprotect_stack((unsigned long) current_thread);
|
||||
os_usr1_process(os_getpid());
|
||||
change_sig(SIGUSR1, 1);
|
||||
|
||||
change_sig(SIGUSR1, 0);
|
||||
enable_timer();
|
||||
free_page(stack);
|
||||
protect_memory(uml_reserved, high_physmem - uml_reserved, 1, 1, 0, 1);
|
||||
task_protections((unsigned long) current_thread);
|
||||
force_flush_all();
|
||||
unblock_signals();
|
||||
}
|
||||
|
||||
void start_thread_tt(struct pt_regs *regs, unsigned long eip,
|
||||
unsigned long esp)
|
||||
{
|
||||
set_fs(USER_DS);
|
||||
flush_tlb_mm(current->mm);
|
||||
PT_REGS_IP(regs) = eip;
|
||||
PT_REGS_SP(regs) = esp;
|
||||
PT_FIX_EXEC_STACK(esp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
57
arch/um/kernel/tt/exec_user.c
Normal file
57
arch/um/kernel/tt/exec_user.c
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sched.h>
|
||||
#include <errno.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "user.h"
|
||||
#include "ptrace_user.h"
|
||||
#include "os.h"
|
||||
|
||||
void do_exec(int old_pid, int new_pid)
|
||||
{
|
||||
unsigned long regs[FRAME_SIZE];
|
||||
int err;
|
||||
|
||||
if((ptrace(PTRACE_ATTACH, new_pid, 0, 0) < 0) ||
|
||||
(ptrace(PTRACE_CONT, new_pid, 0, 0) < 0))
|
||||
tracer_panic("do_exec failed to attach proc - errno = %d",
|
||||
errno);
|
||||
|
||||
CATCH_EINTR(err = waitpid(new_pid, 0, WUNTRACED));
|
||||
if (err < 0)
|
||||
tracer_panic("do_exec failed to attach proc in waitpid - errno = %d",
|
||||
errno);
|
||||
|
||||
if(ptrace_getregs(old_pid, regs) < 0)
|
||||
tracer_panic("do_exec failed to get registers - errno = %d",
|
||||
errno);
|
||||
|
||||
os_kill_ptraced_process(old_pid, 0);
|
||||
|
||||
if (ptrace(PTRACE_OLDSETOPTIONS, new_pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0)
|
||||
tracer_panic("do_exec: PTRACE_SETOPTIONS failed, errno = %d", errno);
|
||||
|
||||
if(ptrace_setregs(new_pid, regs) < 0)
|
||||
tracer_panic("do_exec failed to start new proc - errno = %d",
|
||||
errno);
|
||||
}
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
279
arch/um/kernel/tt/gdb.c
Normal file
279
arch/um/kernel/tt/gdb.c
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include "ptrace_user.h"
|
||||
#include "uml-config.h"
|
||||
#include "kern_constants.h"
|
||||
#include "chan_user.h"
|
||||
#include "init.h"
|
||||
#include "user.h"
|
||||
#include "debug.h"
|
||||
#include "kern_util.h"
|
||||
#include "user_util.h"
|
||||
#include "tt.h"
|
||||
#include "sysdep/thread.h"
|
||||
#include "os.h"
|
||||
|
||||
extern int debugger_pid;
|
||||
extern int debugger_fd;
|
||||
extern int debugger_parent;
|
||||
|
||||
int detach(int pid, int sig)
|
||||
{
|
||||
return(ptrace(PTRACE_DETACH, pid, 0, sig));
|
||||
}
|
||||
|
||||
int attach(int pid)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = ptrace(PTRACE_ATTACH, pid, 0, 0);
|
||||
if(err < 0) return(-errno);
|
||||
else return(err);
|
||||
}
|
||||
|
||||
int cont(int pid)
|
||||
{
|
||||
return(ptrace(PTRACE_CONT, pid, 0, 0));
|
||||
}
|
||||
|
||||
#ifdef UML_CONFIG_PT_PROXY
|
||||
|
||||
int debugger_signal(int status, pid_t pid)
|
||||
{
|
||||
return(debugger_proxy(status, pid));
|
||||
}
|
||||
|
||||
void child_signal(pid_t pid, int status)
|
||||
{
|
||||
child_proxy(pid, status);
|
||||
}
|
||||
|
||||
static void gdb_announce(char *dev_name, int dev)
|
||||
{
|
||||
printf("gdb assigned device '%s'\n", dev_name);
|
||||
}
|
||||
|
||||
static struct chan_opts opts = {
|
||||
.announce = gdb_announce,
|
||||
.xterm_title = "UML kernel debugger",
|
||||
.raw = 0,
|
||||
.tramp_stack = 0,
|
||||
.in_kernel = 0,
|
||||
};
|
||||
|
||||
/* Accessed by the tracing thread, which automatically serializes access */
|
||||
static void *xterm_data;
|
||||
static int xterm_fd;
|
||||
|
||||
extern void *xterm_init(char *, int, struct chan_opts *);
|
||||
extern int xterm_open(int, int, int, void *, char **);
|
||||
extern void xterm_close(int, void *);
|
||||
|
||||
int open_gdb_chan(void)
|
||||
{
|
||||
char stack[UM_KERN_PAGE_SIZE], *dummy;
|
||||
|
||||
opts.tramp_stack = (unsigned long) stack;
|
||||
xterm_data = xterm_init("", 0, &opts);
|
||||
xterm_fd = xterm_open(1, 1, 1, xterm_data, &dummy);
|
||||
return(xterm_fd);
|
||||
}
|
||||
|
||||
static void exit_debugger_cb(void *unused)
|
||||
{
|
||||
if(debugger_pid != -1){
|
||||
if(gdb_pid != -1){
|
||||
fake_child_exit();
|
||||
gdb_pid = -1;
|
||||
}
|
||||
else kill_child_dead(debugger_pid);
|
||||
debugger_pid = -1;
|
||||
if(debugger_parent != -1)
|
||||
detach(debugger_parent, SIGINT);
|
||||
}
|
||||
if(xterm_data != NULL) xterm_close(xterm_fd, xterm_data);
|
||||
}
|
||||
|
||||
static void exit_debugger(void)
|
||||
{
|
||||
initial_thread_cb(exit_debugger_cb, NULL);
|
||||
}
|
||||
|
||||
__uml_exitcall(exit_debugger);
|
||||
|
||||
struct gdb_data {
|
||||
char *str;
|
||||
int err;
|
||||
};
|
||||
|
||||
static void config_gdb_cb(void *arg)
|
||||
{
|
||||
struct gdb_data *data = arg;
|
||||
void *task;
|
||||
int pid;
|
||||
|
||||
data->err = -1;
|
||||
if(debugger_pid != -1) exit_debugger_cb(NULL);
|
||||
if(!strncmp(data->str, "pid,", strlen("pid,"))){
|
||||
data->str += strlen("pid,");
|
||||
pid = strtoul(data->str, NULL, 0);
|
||||
task = cpu_tasks[0].task;
|
||||
debugger_pid = attach_debugger(TASK_EXTERN_PID(task), pid, 0);
|
||||
if(debugger_pid != -1){
|
||||
data->err = 0;
|
||||
gdb_pid = pid;
|
||||
}
|
||||
return;
|
||||
}
|
||||
data->err = 0;
|
||||
debugger_pid = start_debugger(linux_prog, 0, 0, &debugger_fd);
|
||||
init_proxy(debugger_pid, 0, 0);
|
||||
}
|
||||
|
||||
int gdb_config(char *str, char **error_out)
|
||||
{
|
||||
struct gdb_data data;
|
||||
|
||||
if(*str++ != '=') return(-1);
|
||||
data.str = str;
|
||||
initial_thread_cb(config_gdb_cb, &data);
|
||||
return(data.err);
|
||||
}
|
||||
|
||||
void remove_gdb_cb(void *unused)
|
||||
{
|
||||
exit_debugger_cb(NULL);
|
||||
}
|
||||
|
||||
int gdb_remove(int unused, char **error_out)
|
||||
{
|
||||
initial_thread_cb(remove_gdb_cb, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void signal_usr1(int sig)
|
||||
{
|
||||
if(debugger_pid != -1){
|
||||
printf("The debugger is already running\n");
|
||||
return;
|
||||
}
|
||||
debugger_pid = start_debugger(linux_prog, 0, 0, &debugger_fd);
|
||||
init_proxy(debugger_pid, 0, 0);
|
||||
}
|
||||
|
||||
int init_ptrace_proxy(int idle_pid, int startup, int stop)
|
||||
{
|
||||
int pid, status;
|
||||
|
||||
pid = start_debugger(linux_prog, startup, stop, &debugger_fd);
|
||||
status = wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT, NULL);
|
||||
if(pid < 0){
|
||||
cont(idle_pid);
|
||||
return(-1);
|
||||
}
|
||||
init_proxy(pid, 1, status);
|
||||
return(pid);
|
||||
}
|
||||
|
||||
int attach_debugger(int idle_pid, int pid, int stop)
|
||||
{
|
||||
int status = 0, err;
|
||||
|
||||
err = attach(pid);
|
||||
if(err < 0){
|
||||
printf("Failed to attach pid %d, errno = %d\n", pid, -err);
|
||||
return(-1);
|
||||
}
|
||||
if(stop) status = wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT, NULL);
|
||||
init_proxy(pid, 1, status);
|
||||
return(pid);
|
||||
}
|
||||
|
||||
#ifdef notdef /* Put this back in when it does something useful */
|
||||
static int __init uml_gdb_init_setup(char *line, int *add)
|
||||
{
|
||||
gdb_init = uml_strdup(line);
|
||||
return 0;
|
||||
}
|
||||
|
||||
__uml_setup("gdb=", uml_gdb_init_setup,
|
||||
"gdb=<channel description>\n\n"
|
||||
);
|
||||
#endif
|
||||
|
||||
static int __init uml_gdb_pid_setup(char *line, int *add)
|
||||
{
|
||||
gdb_pid = strtoul(line, NULL, 0);
|
||||
*add = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__uml_setup("gdb-pid=", uml_gdb_pid_setup,
|
||||
"gdb-pid=<pid>\n"
|
||||
" gdb-pid is used to attach an external debugger to UML. This may be\n"
|
||||
" an already-running gdb or a debugger-like process like strace.\n\n"
|
||||
);
|
||||
|
||||
#else
|
||||
|
||||
int debugger_signal(int status, pid_t pid){ return(0); }
|
||||
void child_signal(pid_t pid, int status){ }
|
||||
int init_ptrace_proxy(int idle_pid, int startup, int stop)
|
||||
{
|
||||
printf("debug requested when CONFIG_PT_PROXY is off\n");
|
||||
kill_child_dead(idle_pid);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void signal_usr1(int sig)
|
||||
{
|
||||
printf("debug requested when CONFIG_PT_PROXY is off\n");
|
||||
}
|
||||
|
||||
int attach_debugger(int idle_pid, int pid, int stop)
|
||||
{
|
||||
printf("attach_debugger called when CONFIG_PT_PROXY "
|
||||
"is off\n");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
int config_gdb(char *str)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
|
||||
int remove_gdb(void)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
|
||||
int init_parent_proxy(int pid)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
|
||||
void debugger_parent_signal(int status, int pid)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
40
arch/um/kernel/tt/gdb_kern.c
Normal file
40
arch/um/kernel/tt/gdb_kern.c
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/init.h"
|
||||
#include "mconsole_kern.h"
|
||||
|
||||
#ifdef CONFIG_MCONSOLE
|
||||
|
||||
extern int gdb_config(char *str, char **error_out);
|
||||
extern int gdb_remove(int n, char **error_out);
|
||||
|
||||
static struct mc_device gdb_mc = {
|
||||
.list = INIT_LIST_HEAD(gdb_mc.list),
|
||||
.name = "gdb",
|
||||
.config = gdb_config,
|
||||
.remove = gdb_remove,
|
||||
};
|
||||
|
||||
int gdb_mc_init(void)
|
||||
{
|
||||
mconsole_register_dev(&gdb_mc);
|
||||
return(0);
|
||||
}
|
||||
|
||||
__initcall(gdb_mc_init);
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
34
arch/um/kernel/tt/include/mode-tt.h
Normal file
34
arch/um/kernel/tt/include/mode-tt.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#ifndef __MODE_TT_H__
|
||||
#define __MODE_TT_H__
|
||||
|
||||
#include "sysdep/ptrace.h"
|
||||
|
||||
enum { OP_NONE, OP_EXEC, OP_FORK, OP_TRACE_ON, OP_REBOOT, OP_HALT, OP_CB };
|
||||
|
||||
extern int tracing_pid;
|
||||
|
||||
extern int tracer(int (*init_proc)(void *), void *sp);
|
||||
extern void sig_handler_common_tt(int sig, void *sc);
|
||||
extern void syscall_handler_tt(int sig, union uml_pt_regs *regs);
|
||||
extern void reboot_tt(void);
|
||||
extern void halt_tt(void);
|
||||
extern int is_tracer_winch(int pid, int fd, void *data);
|
||||
extern void kill_off_processes_tt(void);
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
52
arch/um/kernel/tt/include/mode_kern-tt.h
Normal file
52
arch/um/kernel/tt/include/mode_kern-tt.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#ifndef __TT_MODE_KERN_H__
|
||||
#define __TT_MODE_KERN_H__
|
||||
|
||||
#include "linux/sched.h"
|
||||
#include "asm/page.h"
|
||||
#include "asm/ptrace.h"
|
||||
#include "asm/uaccess.h"
|
||||
|
||||
extern void switch_to_tt(void *prev, void *next);
|
||||
extern void flush_thread_tt(void);
|
||||
extern void start_thread_tt(struct pt_regs *regs, unsigned long eip,
|
||||
unsigned long esp);
|
||||
extern int copy_thread_tt(int nr, unsigned long clone_flags, unsigned long sp,
|
||||
unsigned long stack_top, struct task_struct *p,
|
||||
struct pt_regs *regs);
|
||||
extern void release_thread_tt(struct task_struct *task);
|
||||
extern void initial_thread_cb_tt(void (*proc)(void *), void *arg);
|
||||
extern void init_idle_tt(void);
|
||||
extern void flush_tlb_kernel_range_tt(unsigned long start, unsigned long end);
|
||||
extern void flush_tlb_kernel_vm_tt(void);
|
||||
extern void __flush_tlb_one_tt(unsigned long addr);
|
||||
extern void flush_tlb_range_tt(struct vm_area_struct *vma,
|
||||
unsigned long start, unsigned long end);
|
||||
extern void flush_tlb_mm_tt(struct mm_struct *mm);
|
||||
extern void force_flush_all_tt(void);
|
||||
extern long execute_syscall_tt(void *r);
|
||||
extern void before_mem_tt(unsigned long brk_start);
|
||||
extern unsigned long set_task_sizes_tt(int arg, unsigned long *host_size_out,
|
||||
unsigned long *task_size_out);
|
||||
extern int start_uml_tt(void);
|
||||
extern int external_pid_tt(struct task_struct *task);
|
||||
extern int thread_pid_tt(struct task_struct *task);
|
||||
|
||||
#define kmem_end_tt (host_task_size - ABOVE_KMEM)
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
29
arch/um/kernel/tt/ksyms.c
Normal file
29
arch/um/kernel/tt/ksyms.c
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/module.h"
|
||||
#include "asm/uaccess.h"
|
||||
#include "mode.h"
|
||||
|
||||
EXPORT_SYMBOL(__do_copy_from_user);
|
||||
EXPORT_SYMBOL(__do_copy_to_user);
|
||||
EXPORT_SYMBOL(__do_strncpy_from_user);
|
||||
EXPORT_SYMBOL(__do_strnlen_user);
|
||||
EXPORT_SYMBOL(__do_clear_user);
|
||||
EXPORT_SYMBOL(clear_user_tt);
|
||||
|
||||
EXPORT_SYMBOL(tracing_pid);
|
||||
EXPORT_SYMBOL(honeypot);
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
35
arch/um/kernel/tt/mem.c
Normal file
35
arch/um/kernel/tt/mem.c
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/stddef.h"
|
||||
#include "linux/mm.h"
|
||||
#include "asm/uaccess.h"
|
||||
#include "mem_user.h"
|
||||
#include "kern_util.h"
|
||||
#include "user_util.h"
|
||||
#include "kern.h"
|
||||
#include "tt.h"
|
||||
|
||||
void before_mem_tt(unsigned long brk_start)
|
||||
{
|
||||
if(debug)
|
||||
remap_data(UML_ROUND_DOWN(&_stext), UML_ROUND_UP(&_etext), 1);
|
||||
remap_data(UML_ROUND_DOWN(&_sdata), UML_ROUND_UP(&_edata), 1);
|
||||
remap_data(UML_ROUND_DOWN(&__bss_start), UML_ROUND_UP(&_end), 1);
|
||||
}
|
||||
|
||||
#define SIZE ((CONFIG_NEST_LEVEL + CONFIG_KERNEL_HALF_GIGS) * 0x20000000)
|
||||
#define START (CONFIG_TOP_ADDR - SIZE)
|
||||
|
||||
unsigned long set_task_sizes_tt(unsigned long *task_size_out)
|
||||
{
|
||||
unsigned long host_task_size;
|
||||
|
||||
/* Round up to the nearest 4M */
|
||||
host_task_size = ROUND_4M((unsigned long) &host_task_size);
|
||||
*task_size_out = START;
|
||||
|
||||
return host_task_size;
|
||||
}
|
||||
50
arch/um/kernel/tt/mem_user.c
Normal file
50
arch/um/kernel/tt/mem_user.c
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
#include "tt.h"
|
||||
#include "mem_user.h"
|
||||
#include "user_util.h"
|
||||
#include "os.h"
|
||||
|
||||
void remap_data(void *segment_start, void *segment_end, int w)
|
||||
{
|
||||
void *addr;
|
||||
unsigned long size;
|
||||
int data, prot;
|
||||
|
||||
if(w) prot = PROT_WRITE;
|
||||
else prot = 0;
|
||||
prot |= PROT_READ | PROT_EXEC;
|
||||
size = (unsigned long) segment_end -
|
||||
(unsigned long) segment_start;
|
||||
data = create_mem_file(size);
|
||||
addr = mmap(NULL, size, PROT_WRITE | PROT_READ, MAP_SHARED, data, 0);
|
||||
if(addr == MAP_FAILED){
|
||||
perror("mapping new data segment");
|
||||
exit(1);
|
||||
}
|
||||
memcpy(addr, segment_start, size);
|
||||
if(switcheroo(data, prot, addr, segment_start, size) < 0){
|
||||
printf("switcheroo failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
461
arch/um/kernel/tt/process_kern.c
Normal file
461
arch/um/kernel/tt/process_kern.c
Normal file
@@ -0,0 +1,461 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/sched.h"
|
||||
#include "linux/signal.h"
|
||||
#include "linux/kernel.h"
|
||||
#include "linux/interrupt.h"
|
||||
#include "linux/ptrace.h"
|
||||
#include "asm/system.h"
|
||||
#include "asm/pgalloc.h"
|
||||
#include "asm/ptrace.h"
|
||||
#include "asm/tlbflush.h"
|
||||
#include "irq_user.h"
|
||||
#include "kern_util.h"
|
||||
#include "user_util.h"
|
||||
#include "os.h"
|
||||
#include "kern.h"
|
||||
#include "sigcontext.h"
|
||||
#include "mem_user.h"
|
||||
#include "tlb.h"
|
||||
#include "mode.h"
|
||||
#include "mode_kern.h"
|
||||
#include "init.h"
|
||||
#include "tt.h"
|
||||
|
||||
void switch_to_tt(void *prev, void *next)
|
||||
{
|
||||
struct task_struct *from, *to, *prev_sched;
|
||||
unsigned long flags;
|
||||
int err, vtalrm, alrm, prof, cpu;
|
||||
char c;
|
||||
|
||||
from = prev;
|
||||
to = next;
|
||||
|
||||
cpu = task_thread_info(from)->cpu;
|
||||
if(cpu == 0)
|
||||
forward_interrupts(to->thread.mode.tt.extern_pid);
|
||||
#ifdef CONFIG_SMP
|
||||
forward_ipi(cpu_data[cpu].ipi_pipe[0], to->thread.mode.tt.extern_pid);
|
||||
#endif
|
||||
local_irq_save(flags);
|
||||
|
||||
vtalrm = change_sig(SIGVTALRM, 0);
|
||||
alrm = change_sig(SIGALRM, 0);
|
||||
prof = change_sig(SIGPROF, 0);
|
||||
|
||||
forward_pending_sigio(to->thread.mode.tt.extern_pid);
|
||||
|
||||
c = 0;
|
||||
|
||||
/* Notice that here we "up" the semaphore on which "to" is waiting, and
|
||||
* below (the read) we wait on this semaphore (which is implemented by
|
||||
* switch_pipe) and go sleeping. Thus, after that, we have resumed in
|
||||
* "to", and can't use any more the value of "from" (which is outdated),
|
||||
* nor the value in "to" (since it was the task which stole us the CPU,
|
||||
* which we don't care about). */
|
||||
|
||||
err = os_write_file(to->thread.mode.tt.switch_pipe[1], &c, sizeof(c));
|
||||
if(err != sizeof(c))
|
||||
panic("write of switch_pipe failed, err = %d", -err);
|
||||
|
||||
if(from->thread.mode.tt.switch_pipe[0] == -1)
|
||||
os_kill_process(os_getpid(), 0);
|
||||
|
||||
err = os_read_file(from->thread.mode.tt.switch_pipe[0], &c, sizeof(c));
|
||||
if(err != sizeof(c))
|
||||
panic("read of switch_pipe failed, errno = %d", -err);
|
||||
|
||||
/* If the process that we have just scheduled away from has exited,
|
||||
* then it needs to be killed here. The reason is that, even though
|
||||
* it will kill itself when it next runs, that may be too late. Its
|
||||
* stack will be freed, possibly before then, and if that happens,
|
||||
* we have a use-after-free situation. So, it gets killed here
|
||||
* in case it has not already killed itself.
|
||||
*/
|
||||
prev_sched = current->thread.prev_sched;
|
||||
if(prev_sched->thread.mode.tt.switch_pipe[0] == -1)
|
||||
os_kill_process(prev_sched->thread.mode.tt.extern_pid, 1);
|
||||
|
||||
change_sig(SIGVTALRM, vtalrm);
|
||||
change_sig(SIGALRM, alrm);
|
||||
change_sig(SIGPROF, prof);
|
||||
|
||||
arch_switch_to_tt(prev_sched, current);
|
||||
|
||||
flush_tlb_all();
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
void release_thread_tt(struct task_struct *task)
|
||||
{
|
||||
int pid = task->thread.mode.tt.extern_pid;
|
||||
|
||||
/*
|
||||
* We first have to kill the other process, before
|
||||
* closing its switch_pipe. Else it might wake up
|
||||
* and receive "EOF" before we could kill it.
|
||||
*/
|
||||
if(os_getpid() != pid)
|
||||
os_kill_process(pid, 0);
|
||||
|
||||
os_close_file(task->thread.mode.tt.switch_pipe[0]);
|
||||
os_close_file(task->thread.mode.tt.switch_pipe[1]);
|
||||
/* use switch_pipe as flag: thread is released */
|
||||
task->thread.mode.tt.switch_pipe[0] = -1;
|
||||
}
|
||||
|
||||
void suspend_new_thread(int fd)
|
||||
{
|
||||
int err;
|
||||
char c;
|
||||
|
||||
os_stop_process(os_getpid());
|
||||
err = os_read_file(fd, &c, sizeof(c));
|
||||
if(err != sizeof(c))
|
||||
panic("read failed in suspend_new_thread, err = %d", -err);
|
||||
}
|
||||
|
||||
void schedule_tail(struct task_struct *prev);
|
||||
|
||||
static void new_thread_handler(int sig)
|
||||
{
|
||||
unsigned long disable;
|
||||
int (*fn)(void *);
|
||||
void *arg;
|
||||
|
||||
fn = current->thread.request.u.thread.proc;
|
||||
arg = current->thread.request.u.thread.arg;
|
||||
|
||||
UPT_SC(¤t->thread.regs.regs) = (void *) (&sig + 1);
|
||||
disable = (1 << (SIGVTALRM - 1)) | (1 << (SIGALRM - 1)) |
|
||||
(1 << (SIGIO - 1)) | (1 << (SIGPROF - 1));
|
||||
SC_SIGMASK(UPT_SC(¤t->thread.regs.regs)) &= ~disable;
|
||||
|
||||
suspend_new_thread(current->thread.mode.tt.switch_pipe[0]);
|
||||
|
||||
force_flush_all();
|
||||
if(current->thread.prev_sched != NULL)
|
||||
schedule_tail(current->thread.prev_sched);
|
||||
current->thread.prev_sched = NULL;
|
||||
|
||||
init_new_thread_signals();
|
||||
enable_timer();
|
||||
free_page(current->thread.temp_stack);
|
||||
set_cmdline("(kernel thread)");
|
||||
|
||||
change_sig(SIGUSR1, 1);
|
||||
change_sig(SIGPROF, 1);
|
||||
local_irq_enable();
|
||||
if(!run_kernel_thread(fn, arg, ¤t->thread.exec_buf))
|
||||
do_exit(0);
|
||||
|
||||
/* XXX No set_user_mode here because a newly execed process will
|
||||
* immediately segfault on its non-existent IP, coming straight back
|
||||
* to the signal handler, which will call set_user_mode on its way
|
||||
* out. This should probably change since it's confusing.
|
||||
*/
|
||||
}
|
||||
|
||||
static int new_thread_proc(void *stack)
|
||||
{
|
||||
/* local_irq_disable is needed to block out signals until this thread is
|
||||
* properly scheduled. Otherwise, the tracing thread will get mighty
|
||||
* upset about any signals that arrive before that.
|
||||
* This has the complication that it sets the saved signal mask in
|
||||
* the sigcontext to block signals. This gets restored when this
|
||||
* thread (or a descendant, since they get a copy of this sigcontext)
|
||||
* returns to userspace.
|
||||
* So, this is compensated for elsewhere.
|
||||
* XXX There is still a small window until local_irq_disable() actually
|
||||
* finishes where signals are possible - shouldn't be a problem in
|
||||
* practice since SIGIO hasn't been forwarded here yet, and the
|
||||
* local_irq_disable should finish before a SIGVTALRM has time to be
|
||||
* delivered.
|
||||
*/
|
||||
|
||||
local_irq_disable();
|
||||
init_new_thread_stack(stack, new_thread_handler);
|
||||
os_usr1_process(os_getpid());
|
||||
change_sig(SIGUSR1, 1);
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* Signal masking - signals are blocked at the start of fork_tramp. They
|
||||
* are re-enabled when finish_fork_handler is entered by fork_tramp hitting
|
||||
* itself with a SIGUSR1. set_user_mode has to be run with SIGUSR1 off,
|
||||
* so it is blocked before it's called. They are re-enabled on sigreturn
|
||||
* despite the fact that they were blocked when the SIGUSR1 was issued because
|
||||
* copy_thread copies the parent's sigcontext, including the signal mask
|
||||
* onto the signal frame.
|
||||
*/
|
||||
|
||||
void finish_fork_handler(int sig)
|
||||
{
|
||||
UPT_SC(¤t->thread.regs.regs) = (void *) (&sig + 1);
|
||||
suspend_new_thread(current->thread.mode.tt.switch_pipe[0]);
|
||||
|
||||
force_flush_all();
|
||||
if(current->thread.prev_sched != NULL)
|
||||
schedule_tail(current->thread.prev_sched);
|
||||
current->thread.prev_sched = NULL;
|
||||
|
||||
enable_timer();
|
||||
change_sig(SIGVTALRM, 1);
|
||||
local_irq_enable();
|
||||
if(current->mm != current->parent->mm)
|
||||
protect_memory(uml_reserved, high_physmem - uml_reserved, 1,
|
||||
1, 0, 1);
|
||||
task_protections((unsigned long) current_thread);
|
||||
|
||||
free_page(current->thread.temp_stack);
|
||||
local_irq_disable();
|
||||
change_sig(SIGUSR1, 0);
|
||||
set_user_mode(current);
|
||||
}
|
||||
|
||||
int fork_tramp(void *stack)
|
||||
{
|
||||
local_irq_disable();
|
||||
arch_init_thread();
|
||||
init_new_thread_stack(stack, finish_fork_handler);
|
||||
|
||||
os_usr1_process(os_getpid());
|
||||
change_sig(SIGUSR1, 1);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int copy_thread_tt(int nr, unsigned long clone_flags, unsigned long sp,
|
||||
unsigned long stack_top, struct task_struct * p,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
int (*tramp)(void *);
|
||||
int new_pid, err;
|
||||
unsigned long stack;
|
||||
|
||||
if(current->thread.forking)
|
||||
tramp = fork_tramp;
|
||||
else {
|
||||
tramp = new_thread_proc;
|
||||
p->thread.request.u.thread = current->thread.request.u.thread;
|
||||
}
|
||||
|
||||
err = os_pipe(p->thread.mode.tt.switch_pipe, 1, 1);
|
||||
if(err < 0){
|
||||
printk("copy_thread : pipe failed, err = %d\n", -err);
|
||||
return(err);
|
||||
}
|
||||
|
||||
stack = alloc_stack(0, 0);
|
||||
if(stack == 0){
|
||||
printk(KERN_ERR "copy_thread : failed to allocate "
|
||||
"temporary stack\n");
|
||||
return(-ENOMEM);
|
||||
}
|
||||
|
||||
clone_flags &= CLONE_VM;
|
||||
p->thread.temp_stack = stack;
|
||||
new_pid = start_fork_tramp(task_stack_page(p), stack, clone_flags, tramp);
|
||||
if(new_pid < 0){
|
||||
printk(KERN_ERR "copy_thread : clone failed - errno = %d\n",
|
||||
-new_pid);
|
||||
return(new_pid);
|
||||
}
|
||||
|
||||
if(current->thread.forking){
|
||||
sc_to_sc(UPT_SC(&p->thread.regs.regs), UPT_SC(®s->regs));
|
||||
SC_SET_SYSCALL_RETURN(UPT_SC(&p->thread.regs.regs), 0);
|
||||
if(sp != 0)
|
||||
SC_SP(UPT_SC(&p->thread.regs.regs)) = sp;
|
||||
}
|
||||
p->thread.mode.tt.extern_pid = new_pid;
|
||||
|
||||
current->thread.request.op = OP_FORK;
|
||||
current->thread.request.u.fork.pid = new_pid;
|
||||
os_usr1_process(os_getpid());
|
||||
|
||||
/* Enable the signal and then disable it to ensure that it is handled
|
||||
* here, and nowhere else.
|
||||
*/
|
||||
change_sig(SIGUSR1, 1);
|
||||
|
||||
change_sig(SIGUSR1, 0);
|
||||
err = 0;
|
||||
return(err);
|
||||
}
|
||||
|
||||
void reboot_tt(void)
|
||||
{
|
||||
current->thread.request.op = OP_REBOOT;
|
||||
os_usr1_process(os_getpid());
|
||||
change_sig(SIGUSR1, 1);
|
||||
}
|
||||
|
||||
void halt_tt(void)
|
||||
{
|
||||
current->thread.request.op = OP_HALT;
|
||||
os_usr1_process(os_getpid());
|
||||
change_sig(SIGUSR1, 1);
|
||||
}
|
||||
|
||||
void kill_off_processes_tt(void)
|
||||
{
|
||||
struct task_struct *p;
|
||||
int me;
|
||||
|
||||
me = os_getpid();
|
||||
for_each_process(p){
|
||||
if(p->thread.mode.tt.extern_pid != me)
|
||||
os_kill_process(p->thread.mode.tt.extern_pid, 0);
|
||||
}
|
||||
if(init_task.thread.mode.tt.extern_pid != me)
|
||||
os_kill_process(init_task.thread.mode.tt.extern_pid, 0);
|
||||
}
|
||||
|
||||
void initial_thread_cb_tt(void (*proc)(void *), void *arg)
|
||||
{
|
||||
if(os_getpid() == tracing_pid){
|
||||
(*proc)(arg);
|
||||
}
|
||||
else {
|
||||
current->thread.request.op = OP_CB;
|
||||
current->thread.request.u.cb.proc = proc;
|
||||
current->thread.request.u.cb.arg = arg;
|
||||
os_usr1_process(os_getpid());
|
||||
change_sig(SIGUSR1, 1);
|
||||
|
||||
change_sig(SIGUSR1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int do_proc_op(void *t, int proc_id)
|
||||
{
|
||||
struct task_struct *task;
|
||||
struct thread_struct *thread;
|
||||
int op, pid;
|
||||
|
||||
task = t;
|
||||
thread = &task->thread;
|
||||
op = thread->request.op;
|
||||
switch(op){
|
||||
case OP_NONE:
|
||||
case OP_TRACE_ON:
|
||||
break;
|
||||
case OP_EXEC:
|
||||
pid = thread->request.u.exec.pid;
|
||||
do_exec(thread->mode.tt.extern_pid, pid);
|
||||
thread->mode.tt.extern_pid = pid;
|
||||
cpu_tasks[task_thread_info(task)->cpu].pid = pid;
|
||||
break;
|
||||
case OP_FORK:
|
||||
attach_process(thread->request.u.fork.pid);
|
||||
break;
|
||||
case OP_CB:
|
||||
(*thread->request.u.cb.proc)(thread->request.u.cb.arg);
|
||||
break;
|
||||
case OP_REBOOT:
|
||||
case OP_HALT:
|
||||
break;
|
||||
default:
|
||||
tracer_panic("Bad op in do_proc_op");
|
||||
break;
|
||||
}
|
||||
thread->request.op = OP_NONE;
|
||||
return(op);
|
||||
}
|
||||
|
||||
void init_idle_tt(void)
|
||||
{
|
||||
default_idle();
|
||||
}
|
||||
|
||||
extern void start_kernel(void);
|
||||
|
||||
static int start_kernel_proc(void *unused)
|
||||
{
|
||||
int pid;
|
||||
|
||||
block_signals();
|
||||
pid = os_getpid();
|
||||
|
||||
cpu_tasks[0].pid = pid;
|
||||
cpu_tasks[0].task = current;
|
||||
#ifdef CONFIG_SMP
|
||||
cpu_online_map = cpumask_of_cpu(0);
|
||||
#endif
|
||||
if(debug) os_stop_process(pid);
|
||||
start_kernel();
|
||||
return(0);
|
||||
}
|
||||
|
||||
void set_tracing(void *task, int tracing)
|
||||
{
|
||||
((struct task_struct *) task)->thread.mode.tt.tracing = tracing;
|
||||
}
|
||||
|
||||
int is_tracing(void *t)
|
||||
{
|
||||
return (((struct task_struct *) t)->thread.mode.tt.tracing);
|
||||
}
|
||||
|
||||
int set_user_mode(void *t)
|
||||
{
|
||||
struct task_struct *task;
|
||||
|
||||
task = t ? t : current;
|
||||
if(task->thread.mode.tt.tracing)
|
||||
return(1);
|
||||
task->thread.request.op = OP_TRACE_ON;
|
||||
os_usr1_process(os_getpid());
|
||||
return(0);
|
||||
}
|
||||
|
||||
void set_init_pid(int pid)
|
||||
{
|
||||
int err;
|
||||
|
||||
init_task.thread.mode.tt.extern_pid = pid;
|
||||
err = os_pipe(init_task.thread.mode.tt.switch_pipe, 1, 1);
|
||||
if(err)
|
||||
panic("Can't create switch pipe for init_task, errno = %d",
|
||||
-err);
|
||||
}
|
||||
|
||||
int start_uml_tt(void)
|
||||
{
|
||||
void *sp;
|
||||
int pages;
|
||||
|
||||
pages = (1 << CONFIG_KERNEL_STACK_ORDER);
|
||||
sp = task_stack_page(&init_task) +
|
||||
pages * PAGE_SIZE - sizeof(unsigned long);
|
||||
return(tracer(start_kernel_proc, sp));
|
||||
}
|
||||
|
||||
int external_pid_tt(struct task_struct *task)
|
||||
{
|
||||
return(task->thread.mode.tt.extern_pid);
|
||||
}
|
||||
|
||||
int thread_pid_tt(struct task_struct *task)
|
||||
{
|
||||
return(task->thread.mode.tt.extern_pid);
|
||||
}
|
||||
|
||||
int is_valid_pid(int pid)
|
||||
{
|
||||
struct task_struct *task;
|
||||
|
||||
read_lock(&tasklist_lock);
|
||||
for_each_process(task){
|
||||
if(task->thread.mode.tt.extern_pid == pid){
|
||||
read_unlock(&tasklist_lock);
|
||||
return(1);
|
||||
}
|
||||
}
|
||||
read_unlock(&tasklist_lock);
|
||||
return(0);
|
||||
}
|
||||
10
arch/um/kernel/tt/ptproxy/Makefile
Normal file
10
arch/um/kernel/tt/ptproxy/Makefile
Normal file
@@ -0,0 +1,10 @@
|
||||
#
|
||||
# Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
# Licensed under the GPL
|
||||
#
|
||||
|
||||
obj-y = proxy.o ptrace.o sysdep.o wait.o
|
||||
|
||||
USER_OBJS := $(obj-y)
|
||||
|
||||
include arch/um/scripts/Makefile.rules
|
||||
377
arch/um/kernel/tt/ptproxy/proxy.c
Normal file
377
arch/um/kernel/tt/ptproxy/proxy.c
Normal file
@@ -0,0 +1,377 @@
|
||||
/**********************************************************************
|
||||
proxy.c
|
||||
|
||||
Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing
|
||||
terms and conditions.
|
||||
|
||||
Jeff Dike (jdike@karaya.com) : Modified for integration into uml
|
||||
**********************************************************************/
|
||||
|
||||
/* XXX This file shouldn't refer to CONFIG_* */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <asm/unistd.h>
|
||||
#include "ptrace_user.h"
|
||||
|
||||
#include "ptproxy.h"
|
||||
#include "sysdep.h"
|
||||
#include "wait.h"
|
||||
|
||||
#include "user_util.h"
|
||||
#include "user.h"
|
||||
#include "os.h"
|
||||
#include "tempfile.h"
|
||||
|
||||
static int debugger_wait(debugger_state *debugger, int *status, int options,
|
||||
int (*syscall)(debugger_state *debugger, pid_t child),
|
||||
int (*normal_return)(debugger_state *debugger,
|
||||
pid_t unused),
|
||||
int (*wait_return)(debugger_state *debugger,
|
||||
pid_t unused))
|
||||
{
|
||||
if(debugger->real_wait){
|
||||
debugger->handle_trace = normal_return;
|
||||
syscall_continue(debugger->pid);
|
||||
debugger->real_wait = 0;
|
||||
return(1);
|
||||
}
|
||||
debugger->wait_status_ptr = status;
|
||||
debugger->wait_options = options;
|
||||
if((debugger->debugee != NULL) && debugger->debugee->event){
|
||||
syscall_continue(debugger->pid);
|
||||
wait_for_stop(debugger->pid, SIGTRAP, PTRACE_SYSCALL,
|
||||
NULL);
|
||||
(*wait_return)(debugger, -1);
|
||||
return(0);
|
||||
}
|
||||
else if(debugger->wait_options & WNOHANG){
|
||||
syscall_cancel(debugger->pid, 0);
|
||||
debugger->handle_trace = syscall;
|
||||
return(0);
|
||||
}
|
||||
else {
|
||||
syscall_pause(debugger->pid);
|
||||
debugger->handle_trace = wait_return;
|
||||
debugger->waiting = 1;
|
||||
}
|
||||
return(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle debugger trap, i.e. syscall.
|
||||
*/
|
||||
|
||||
int debugger_syscall(debugger_state *debugger, pid_t child)
|
||||
{
|
||||
long arg1, arg2, arg3, arg4, arg5, result;
|
||||
int syscall, ret = 0;
|
||||
|
||||
syscall = get_syscall(debugger->pid, &arg1, &arg2, &arg3, &arg4,
|
||||
&arg5);
|
||||
|
||||
switch(syscall){
|
||||
case __NR_execve:
|
||||
/* execve never returns */
|
||||
debugger->handle_trace = debugger_syscall;
|
||||
break;
|
||||
|
||||
case __NR_ptrace:
|
||||
if(debugger->debugee->pid != 0) arg2 = debugger->debugee->pid;
|
||||
if(!debugger->debugee->in_context)
|
||||
child = debugger->debugee->pid;
|
||||
result = proxy_ptrace(debugger, arg1, arg2, arg3, arg4, child,
|
||||
&ret);
|
||||
syscall_cancel(debugger->pid, result);
|
||||
debugger->handle_trace = debugger_syscall;
|
||||
return(ret);
|
||||
|
||||
#ifdef __NR_waitpid
|
||||
case __NR_waitpid:
|
||||
#endif
|
||||
case __NR_wait4:
|
||||
if(!debugger_wait(debugger, (int *) arg2, arg3,
|
||||
debugger_syscall, debugger_normal_return,
|
||||
proxy_wait_return))
|
||||
return(0);
|
||||
break;
|
||||
|
||||
case __NR_kill:
|
||||
if(!debugger->debugee->in_context)
|
||||
child = debugger->debugee->pid;
|
||||
if(arg1 == debugger->debugee->pid){
|
||||
result = kill(child, arg2);
|
||||
syscall_cancel(debugger->pid, result);
|
||||
debugger->handle_trace = debugger_syscall;
|
||||
return(0);
|
||||
}
|
||||
else debugger->handle_trace = debugger_normal_return;
|
||||
break;
|
||||
|
||||
default:
|
||||
debugger->handle_trace = debugger_normal_return;
|
||||
}
|
||||
|
||||
syscall_continue(debugger->pid);
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* Used by the tracing thread */
|
||||
static debugger_state parent;
|
||||
static int parent_syscall(debugger_state *debugger, int pid);
|
||||
|
||||
int init_parent_proxy(int pid)
|
||||
{
|
||||
parent = ((debugger_state) { .pid = pid,
|
||||
.wait_options = 0,
|
||||
.wait_status_ptr = NULL,
|
||||
.waiting = 0,
|
||||
.real_wait = 0,
|
||||
.expecting_child = 0,
|
||||
.handle_trace = parent_syscall,
|
||||
.debugee = NULL } );
|
||||
return(0);
|
||||
}
|
||||
|
||||
int parent_normal_return(debugger_state *debugger, pid_t unused)
|
||||
{
|
||||
debugger->handle_trace = parent_syscall;
|
||||
syscall_continue(debugger->pid);
|
||||
return(0);
|
||||
}
|
||||
|
||||
static int parent_syscall(debugger_state *debugger, int pid)
|
||||
{
|
||||
long arg1, arg2, arg3, arg4, arg5;
|
||||
int syscall;
|
||||
|
||||
syscall = get_syscall(pid, &arg1, &arg2, &arg3, &arg4, &arg5);
|
||||
|
||||
if((syscall == __NR_wait4)
|
||||
#ifdef __NR_waitpid
|
||||
|| (syscall == __NR_waitpid)
|
||||
#endif
|
||||
){
|
||||
debugger_wait(&parent, (int *) arg2, arg3, parent_syscall,
|
||||
parent_normal_return, parent_wait_return);
|
||||
}
|
||||
else ptrace(PTRACE_SYSCALL, pid, 0, 0);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int debugger_normal_return(debugger_state *debugger, pid_t unused)
|
||||
{
|
||||
debugger->handle_trace = debugger_syscall;
|
||||
syscall_continue(debugger->pid);
|
||||
return(0);
|
||||
}
|
||||
|
||||
void debugger_cancelled_return(debugger_state *debugger, int result)
|
||||
{
|
||||
debugger->handle_trace = debugger_syscall;
|
||||
syscall_set_result(debugger->pid, result);
|
||||
syscall_continue(debugger->pid);
|
||||
}
|
||||
|
||||
/* Used by the tracing thread */
|
||||
static debugger_state debugger;
|
||||
static debugee_state debugee;
|
||||
|
||||
void init_proxy (pid_t debugger_pid, int stopped, int status)
|
||||
{
|
||||
debugger.pid = debugger_pid;
|
||||
debugger.handle_trace = debugger_syscall;
|
||||
debugger.debugee = &debugee;
|
||||
debugger.waiting = 0;
|
||||
debugger.real_wait = 0;
|
||||
debugger.expecting_child = 0;
|
||||
|
||||
debugee.pid = 0;
|
||||
debugee.traced = 0;
|
||||
debugee.stopped = stopped;
|
||||
debugee.event = 0;
|
||||
debugee.zombie = 0;
|
||||
debugee.died = 0;
|
||||
debugee.wait_status = status;
|
||||
debugee.in_context = 1;
|
||||
}
|
||||
|
||||
int debugger_proxy(int status, int pid)
|
||||
{
|
||||
int ret = 0, sig;
|
||||
|
||||
if(WIFSTOPPED(status)){
|
||||
sig = WSTOPSIG(status);
|
||||
if (sig == SIGTRAP)
|
||||
ret = (*debugger.handle_trace)(&debugger, pid);
|
||||
|
||||
else if(sig == SIGCHLD){
|
||||
if(debugger.expecting_child){
|
||||
ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig);
|
||||
debugger.expecting_child = 0;
|
||||
}
|
||||
else if(debugger.waiting)
|
||||
real_wait_return(&debugger);
|
||||
else {
|
||||
ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig);
|
||||
debugger.real_wait = 1;
|
||||
}
|
||||
}
|
||||
else ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig);
|
||||
}
|
||||
else if(WIFEXITED(status)){
|
||||
tracer_panic("debugger (pid %d) exited with status %d",
|
||||
debugger.pid, WEXITSTATUS(status));
|
||||
}
|
||||
else if(WIFSIGNALED(status)){
|
||||
tracer_panic("debugger (pid %d) exited with signal %d",
|
||||
debugger.pid, WTERMSIG(status));
|
||||
}
|
||||
else {
|
||||
tracer_panic("proxy got unknown status (0x%x) on debugger "
|
||||
"(pid %d)", status, debugger.pid);
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
void child_proxy(pid_t pid, int status)
|
||||
{
|
||||
debugee.event = 1;
|
||||
debugee.wait_status = status;
|
||||
|
||||
if(WIFSTOPPED(status)){
|
||||
debugee.stopped = 1;
|
||||
debugger.expecting_child = 1;
|
||||
kill(debugger.pid, SIGCHLD);
|
||||
}
|
||||
else if(WIFEXITED(status) || WIFSIGNALED(status)){
|
||||
debugee.zombie = 1;
|
||||
debugger.expecting_child = 1;
|
||||
kill(debugger.pid, SIGCHLD);
|
||||
}
|
||||
else panic("proxy got unknown status (0x%x) on child (pid %d)",
|
||||
status, pid);
|
||||
}
|
||||
|
||||
void debugger_parent_signal(int status, int pid)
|
||||
{
|
||||
int sig;
|
||||
|
||||
if(WIFSTOPPED(status)){
|
||||
sig = WSTOPSIG(status);
|
||||
if(sig == SIGTRAP) (*parent.handle_trace)(&parent, pid);
|
||||
else ptrace(PTRACE_SYSCALL, pid, 0, sig);
|
||||
}
|
||||
}
|
||||
|
||||
void fake_child_exit(void)
|
||||
{
|
||||
int status, pid;
|
||||
|
||||
child_proxy(1, W_EXITCODE(0, 0));
|
||||
while(debugger.waiting == 1){
|
||||
CATCH_EINTR(pid = waitpid(debugger.pid, &status, WUNTRACED));
|
||||
if(pid != debugger.pid){
|
||||
printk("fake_child_exit - waitpid failed, "
|
||||
"errno = %d\n", errno);
|
||||
return;
|
||||
}
|
||||
debugger_proxy(status, debugger.pid);
|
||||
}
|
||||
CATCH_EINTR(pid = waitpid(debugger.pid, &status, WUNTRACED));
|
||||
if(pid != debugger.pid){
|
||||
printk("fake_child_exit - waitpid failed, "
|
||||
"errno = %d\n", errno);
|
||||
return;
|
||||
}
|
||||
if(ptrace(PTRACE_DETACH, debugger.pid, 0, SIGCONT) < 0)
|
||||
printk("fake_child_exit - PTRACE_DETACH failed, errno = %d\n",
|
||||
errno);
|
||||
}
|
||||
|
||||
char gdb_init_string[] =
|
||||
"att 1 \n\
|
||||
b panic \n\
|
||||
b stop \n\
|
||||
handle SIGWINCH nostop noprint pass \n\
|
||||
";
|
||||
|
||||
int start_debugger(char *prog, int startup, int stop, int *fd_out)
|
||||
{
|
||||
int slave, child;
|
||||
|
||||
slave = open_gdb_chan();
|
||||
child = fork();
|
||||
if(child == 0){
|
||||
char *tempname = NULL;
|
||||
int fd;
|
||||
|
||||
if(setsid() < 0) perror("setsid");
|
||||
if((dup2(slave, 0) < 0) || (dup2(slave, 1) < 0) ||
|
||||
(dup2(slave, 2) < 0)){
|
||||
printk("start_debugger : dup2 failed, errno = %d\n",
|
||||
errno);
|
||||
exit(1);
|
||||
}
|
||||
if(ioctl(0, TIOCSCTTY, 0) < 0){
|
||||
printk("start_debugger : TIOCSCTTY failed, "
|
||||
"errno = %d\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
if(tcsetpgrp (1, os_getpid()) < 0){
|
||||
printk("start_debugger : tcsetpgrp failed, "
|
||||
"errno = %d\n", errno);
|
||||
#ifdef notdef
|
||||
exit(1);
|
||||
#endif
|
||||
}
|
||||
fd = make_tempfile("/tmp/gdb_init-XXXXXX", &tempname, 0);
|
||||
if(fd < 0){
|
||||
printk("start_debugger : make_tempfile failed,"
|
||||
"err = %d\n", -fd);
|
||||
exit(1);
|
||||
}
|
||||
os_write_file(fd, gdb_init_string, sizeof(gdb_init_string) - 1);
|
||||
if(startup){
|
||||
if(stop){
|
||||
os_write_file(fd, "b start_kernel\n",
|
||||
strlen("b start_kernel\n"));
|
||||
}
|
||||
os_write_file(fd, "c\n", strlen("c\n"));
|
||||
}
|
||||
if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){
|
||||
printk("start_debugger : PTRACE_TRACEME failed, "
|
||||
"errno = %d\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
execlp("gdb", "gdb", "--command", tempname, prog, NULL);
|
||||
printk("start_debugger : exec of gdb failed, errno = %d\n",
|
||||
errno);
|
||||
}
|
||||
if(child < 0){
|
||||
printk("start_debugger : fork for gdb failed, errno = %d\n",
|
||||
errno);
|
||||
return(-1);
|
||||
}
|
||||
*fd_out = slave;
|
||||
return(child);
|
||||
}
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
61
arch/um/kernel/tt/ptproxy/ptproxy.h
Normal file
61
arch/um/kernel/tt/ptproxy/ptproxy.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/**********************************************************************
|
||||
ptproxy.h
|
||||
|
||||
Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing
|
||||
terms and conditions.
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef __PTPROXY_H
|
||||
#define __PTPROXY_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef struct debugger debugger_state;
|
||||
typedef struct debugee debugee_state;
|
||||
|
||||
struct debugger
|
||||
{
|
||||
pid_t pid;
|
||||
int wait_options;
|
||||
int *wait_status_ptr;
|
||||
unsigned int waiting : 1;
|
||||
unsigned int real_wait : 1;
|
||||
unsigned int expecting_child : 1;
|
||||
int (*handle_trace) (debugger_state *, pid_t);
|
||||
|
||||
debugee_state *debugee;
|
||||
};
|
||||
|
||||
struct debugee
|
||||
{
|
||||
pid_t pid;
|
||||
int wait_status;
|
||||
unsigned int died : 1;
|
||||
unsigned int event : 1;
|
||||
unsigned int stopped : 1;
|
||||
unsigned int trace_singlestep : 1;
|
||||
unsigned int trace_syscall : 1;
|
||||
unsigned int traced : 1;
|
||||
unsigned int zombie : 1;
|
||||
unsigned int in_context : 1;
|
||||
};
|
||||
|
||||
extern int debugger_syscall(debugger_state *debugger, pid_t pid);
|
||||
extern int debugger_normal_return (debugger_state *debugger, pid_t unused);
|
||||
|
||||
extern long proxy_ptrace (struct debugger *, int, pid_t, long, long, pid_t,
|
||||
int *strace_out);
|
||||
extern void debugger_cancelled_return(debugger_state *debugger, int result);
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
238
arch/um/kernel/tt/ptproxy/ptrace.c
Normal file
238
arch/um/kernel/tt/ptproxy/ptrace.c
Normal file
@@ -0,0 +1,238 @@
|
||||
/**********************************************************************
|
||||
ptrace.c
|
||||
|
||||
Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing
|
||||
terms and conditions.
|
||||
|
||||
Jeff Dike (jdike@karaya.com) : Modified for integration into uml
|
||||
**********************************************************************/
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "ptproxy.h"
|
||||
#include "debug.h"
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "ptrace_user.h"
|
||||
#include "tt.h"
|
||||
#include "os.h"
|
||||
|
||||
long proxy_ptrace(struct debugger *debugger, int arg1, pid_t arg2,
|
||||
long arg3, long arg4, pid_t child, int *ret)
|
||||
{
|
||||
sigset_t relay;
|
||||
long result;
|
||||
int status;
|
||||
|
||||
*ret = 0;
|
||||
if(debugger->debugee->died) return(-ESRCH);
|
||||
|
||||
switch(arg1){
|
||||
case PTRACE_ATTACH:
|
||||
if(debugger->debugee->traced) return(-EPERM);
|
||||
|
||||
debugger->debugee->pid = arg2;
|
||||
debugger->debugee->traced = 1;
|
||||
|
||||
if(is_valid_pid(arg2) && (arg2 != child)){
|
||||
debugger->debugee->in_context = 0;
|
||||
kill(arg2, SIGSTOP);
|
||||
debugger->debugee->event = 1;
|
||||
debugger->debugee->wait_status = W_STOPCODE(SIGSTOP);
|
||||
}
|
||||
else {
|
||||
debugger->debugee->in_context = 1;
|
||||
if(debugger->debugee->stopped)
|
||||
child_proxy(child, W_STOPCODE(SIGSTOP));
|
||||
else kill(child, SIGSTOP);
|
||||
}
|
||||
|
||||
return(0);
|
||||
|
||||
case PTRACE_DETACH:
|
||||
if(!debugger->debugee->traced) return(-EPERM);
|
||||
|
||||
debugger->debugee->traced = 0;
|
||||
debugger->debugee->pid = 0;
|
||||
if(!debugger->debugee->in_context)
|
||||
kill(child, SIGCONT);
|
||||
|
||||
return(0);
|
||||
|
||||
case PTRACE_CONT:
|
||||
if(!debugger->debugee->in_context) return(-EPERM);
|
||||
*ret = PTRACE_CONT;
|
||||
return(ptrace(PTRACE_CONT, child, arg3, arg4));
|
||||
|
||||
#ifdef UM_HAVE_GETFPREGS
|
||||
case PTRACE_GETFPREGS:
|
||||
{
|
||||
long regs[FP_FRAME_SIZE];
|
||||
int i, result;
|
||||
|
||||
result = ptrace(PTRACE_GETFPREGS, child, 0, regs);
|
||||
if(result == -1) return(-errno);
|
||||
|
||||
for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
|
||||
ptrace(PTRACE_POKEDATA, debugger->pid, arg4 + 4 * i,
|
||||
regs[i]);
|
||||
return(result);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef UM_HAVE_GETFPXREGS
|
||||
case PTRACE_GETFPXREGS:
|
||||
{
|
||||
long regs[FPX_FRAME_SIZE];
|
||||
int i, result;
|
||||
|
||||
result = ptrace(PTRACE_GETFPXREGS, child, 0, regs);
|
||||
if(result == -1) return(-errno);
|
||||
|
||||
for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
|
||||
ptrace(PTRACE_POKEDATA, debugger->pid, arg4 + 4 * i,
|
||||
regs[i]);
|
||||
return(result);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef UM_HAVE_GETREGS
|
||||
case PTRACE_GETREGS:
|
||||
{
|
||||
long regs[FRAME_SIZE];
|
||||
int i, result;
|
||||
|
||||
result = ptrace(PTRACE_GETREGS, child, 0, regs);
|
||||
if(result == -1) return(-errno);
|
||||
|
||||
for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
|
||||
ptrace (PTRACE_POKEDATA, debugger->pid,
|
||||
arg4 + 4 * i, regs[i]);
|
||||
return(result);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case PTRACE_KILL:
|
||||
result = ptrace(PTRACE_KILL, child, arg3, arg4);
|
||||
if(result == -1) return(-errno);
|
||||
|
||||
return(result);
|
||||
|
||||
case PTRACE_PEEKDATA:
|
||||
case PTRACE_PEEKTEXT:
|
||||
case PTRACE_PEEKUSR:
|
||||
/* The value being read out could be -1, so we have to
|
||||
* check errno to see if there's an error, and zero it
|
||||
* beforehand so we're not faked out by an old error
|
||||
*/
|
||||
|
||||
errno = 0;
|
||||
result = ptrace(arg1, child, arg3, 0);
|
||||
if((result == -1) && (errno != 0)) return(-errno);
|
||||
|
||||
result = ptrace(PTRACE_POKEDATA, debugger->pid, arg4, result);
|
||||
if(result == -1) return(-errno);
|
||||
|
||||
return(result);
|
||||
|
||||
case PTRACE_POKEDATA:
|
||||
case PTRACE_POKETEXT:
|
||||
case PTRACE_POKEUSR:
|
||||
result = ptrace(arg1, child, arg3, arg4);
|
||||
if(result == -1) return(-errno);
|
||||
|
||||
if(arg1 == PTRACE_POKEUSR) ptrace_pokeuser(arg3, arg4);
|
||||
return(result);
|
||||
|
||||
#ifdef UM_HAVE_SETFPREGS
|
||||
case PTRACE_SETFPREGS:
|
||||
{
|
||||
long regs[FP_FRAME_SIZE];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
|
||||
regs[i] = ptrace (PTRACE_PEEKDATA, debugger->pid,
|
||||
arg4 + 4 * i, 0);
|
||||
result = ptrace(PTRACE_SETFPREGS, child, 0, regs);
|
||||
if(result == -1) return(-errno);
|
||||
|
||||
return(result);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef UM_HAVE_SETFPXREGS
|
||||
case PTRACE_SETFPXREGS:
|
||||
{
|
||||
long regs[FPX_FRAME_SIZE];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
|
||||
regs[i] = ptrace (PTRACE_PEEKDATA, debugger->pid,
|
||||
arg4 + 4 * i, 0);
|
||||
result = ptrace(PTRACE_SETFPXREGS, child, 0, regs);
|
||||
if(result == -1) return(-errno);
|
||||
|
||||
return(result);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef UM_HAVE_SETREGS
|
||||
case PTRACE_SETREGS:
|
||||
{
|
||||
long regs[FRAME_SIZE];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
|
||||
regs[i] = ptrace(PTRACE_PEEKDATA, debugger->pid,
|
||||
arg4 + 4 * i, 0);
|
||||
result = ptrace(PTRACE_SETREGS, child, 0, regs);
|
||||
if(result == -1) return(-errno);
|
||||
|
||||
return(result);
|
||||
}
|
||||
#endif
|
||||
|
||||
case PTRACE_SINGLESTEP:
|
||||
if(!debugger->debugee->in_context) return(-EPERM);
|
||||
sigemptyset(&relay);
|
||||
sigaddset(&relay, SIGSEGV);
|
||||
sigaddset(&relay, SIGILL);
|
||||
sigaddset(&relay, SIGBUS);
|
||||
result = ptrace(PTRACE_SINGLESTEP, child, arg3, arg4);
|
||||
if(result == -1) return(-errno);
|
||||
|
||||
status = wait_for_stop(child, SIGTRAP, PTRACE_SINGLESTEP,
|
||||
&relay);
|
||||
child_proxy(child, status);
|
||||
return(result);
|
||||
|
||||
case PTRACE_SYSCALL:
|
||||
if(!debugger->debugee->in_context) return(-EPERM);
|
||||
result = ptrace(PTRACE_SYSCALL, child, arg3, arg4);
|
||||
if(result == -1) return(-errno);
|
||||
|
||||
*ret = PTRACE_SYSCALL;
|
||||
return(result);
|
||||
|
||||
case PTRACE_TRACEME:
|
||||
default:
|
||||
return(-EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
71
arch/um/kernel/tt/ptproxy/sysdep.c
Normal file
71
arch/um/kernel/tt/ptproxy/sysdep.c
Normal file
@@ -0,0 +1,71 @@
|
||||
/**********************************************************************
|
||||
sysdep.c
|
||||
|
||||
Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing
|
||||
terms and conditions.
|
||||
**********************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <linux/unistd.h>
|
||||
#include "ptrace_user.h"
|
||||
#include "user_util.h"
|
||||
#include "user.h"
|
||||
#include "os.h"
|
||||
|
||||
int get_syscall(pid_t pid, long *arg1, long *arg2, long *arg3, long *arg4,
|
||||
long *arg5)
|
||||
{
|
||||
*arg1 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG1_OFFSET, 0);
|
||||
*arg2 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG2_OFFSET, 0);
|
||||
*arg3 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG3_OFFSET, 0);
|
||||
*arg4 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG4_OFFSET, 0);
|
||||
*arg5 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG5_OFFSET, 0);
|
||||
return(ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_NR_OFFSET, 0));
|
||||
}
|
||||
|
||||
void syscall_cancel(pid_t pid, int result)
|
||||
{
|
||||
if((ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET,
|
||||
__NR_getpid) < 0) ||
|
||||
(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) ||
|
||||
(wait_for_stop(pid, SIGTRAP, PTRACE_SYSCALL, NULL) < 0) ||
|
||||
(ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET, result) < 0) ||
|
||||
(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0))
|
||||
printk("ptproxy: couldn't cancel syscall: errno = %d\n",
|
||||
errno);
|
||||
}
|
||||
|
||||
void syscall_set_result(pid_t pid, long result)
|
||||
{
|
||||
ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET, result);
|
||||
}
|
||||
|
||||
void syscall_continue(pid_t pid)
|
||||
{
|
||||
ptrace(PTRACE_SYSCALL, pid, 0, 0);
|
||||
}
|
||||
|
||||
int syscall_pause(pid_t pid)
|
||||
{
|
||||
if(ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET, __NR_pause) < 0){
|
||||
printk("syscall_change - ptrace failed, errno = %d\n", errno);
|
||||
return(-1);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
25
arch/um/kernel/tt/ptproxy/sysdep.h
Normal file
25
arch/um/kernel/tt/ptproxy/sysdep.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/**********************************************************************
|
||||
sysdep.h
|
||||
|
||||
Copyright (C) 1999 Lars Brinkhoff.
|
||||
Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
|
||||
See the file COPYING for licensing terms and conditions.
|
||||
**********************************************************************/
|
||||
|
||||
extern int get_syscall(pid_t pid, long *arg1, long *arg2, long *arg3,
|
||||
long *arg4, long *arg5);
|
||||
extern void syscall_cancel (pid_t pid, long result);
|
||||
extern void syscall_set_result (pid_t pid, long result);
|
||||
extern void syscall_continue (pid_t pid);
|
||||
extern int syscall_pause(pid_t pid);
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
86
arch/um/kernel/tt/ptproxy/wait.c
Normal file
86
arch/um/kernel/tt/ptproxy/wait.c
Normal file
@@ -0,0 +1,86 @@
|
||||
/**********************************************************************
|
||||
wait.c
|
||||
|
||||
Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing
|
||||
terms and conditions.
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "ptproxy.h"
|
||||
#include "sysdep.h"
|
||||
#include "wait.h"
|
||||
#include "user_util.h"
|
||||
#include "ptrace_user.h"
|
||||
#include "sysdep/ptrace.h"
|
||||
#include "sysdep/sigcontext.h"
|
||||
|
||||
int proxy_wait_return(struct debugger *debugger, pid_t unused)
|
||||
{
|
||||
debugger->waiting = 0;
|
||||
|
||||
if(debugger->debugee->died || (debugger->wait_options & __WCLONE)){
|
||||
debugger_cancelled_return(debugger, -ECHILD);
|
||||
return(0);
|
||||
}
|
||||
|
||||
if(debugger->debugee->zombie && debugger->debugee->event)
|
||||
debugger->debugee->died = 1;
|
||||
|
||||
if(debugger->debugee->event){
|
||||
debugger->debugee->event = 0;
|
||||
ptrace(PTRACE_POKEDATA, debugger->pid,
|
||||
debugger->wait_status_ptr,
|
||||
debugger->debugee->wait_status);
|
||||
/* if (wait4)
|
||||
ptrace (PTRACE_POKEDATA, pid, rusage_ptr, ...); */
|
||||
debugger_cancelled_return(debugger, debugger->debugee->pid);
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* pause will return -EINTR, which happens to be right for wait */
|
||||
debugger_normal_return(debugger, -1);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int parent_wait_return(struct debugger *debugger, pid_t unused)
|
||||
{
|
||||
return(debugger_normal_return(debugger, -1));
|
||||
}
|
||||
|
||||
int real_wait_return(struct debugger *debugger)
|
||||
{
|
||||
unsigned long ip;
|
||||
int pid;
|
||||
|
||||
pid = debugger->pid;
|
||||
|
||||
ip = ptrace(PTRACE_PEEKUSR, pid, PT_IP_OFFSET, 0);
|
||||
IP_RESTART_SYSCALL(ip);
|
||||
|
||||
if(ptrace(PTRACE_POKEUSR, pid, PT_IP_OFFSET, ip) < 0)
|
||||
tracer_panic("real_wait_return : Failed to restart system "
|
||||
"call, errno = %d\n", errno);
|
||||
|
||||
if((ptrace(PTRACE_SYSCALL, debugger->pid, 0, SIGCHLD) < 0) ||
|
||||
(ptrace(PTRACE_SYSCALL, debugger->pid, 0, 0) < 0) ||
|
||||
(ptrace(PTRACE_SYSCALL, debugger->pid, 0, 0) < 0) ||
|
||||
debugger_normal_return(debugger, -1))
|
||||
tracer_panic("real_wait_return : gdb failed to wait, "
|
||||
"errno = %d\n", errno);
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
15
arch/um/kernel/tt/ptproxy/wait.h
Normal file
15
arch/um/kernel/tt/ptproxy/wait.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/**********************************************************************
|
||||
wait.h
|
||||
|
||||
Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing
|
||||
terms and conditions.
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef __PTPROXY_WAIT_H
|
||||
#define __PTPROXY_WAIT_H
|
||||
|
||||
extern int proxy_wait_return(struct debugger *debugger, pid_t unused);
|
||||
extern int real_wait_return(struct debugger *debugger);
|
||||
extern int parent_wait_return(struct debugger *debugger, pid_t unused);
|
||||
|
||||
#endif
|
||||
46
arch/um/kernel/tt/syscall_kern.c
Normal file
46
arch/um/kernel/tt/syscall_kern.c
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/types.h"
|
||||
#include "linux/utime.h"
|
||||
#include "linux/sys.h"
|
||||
#include "linux/ptrace.h"
|
||||
#include "asm/unistd.h"
|
||||
#include "asm/ptrace.h"
|
||||
#include "asm/uaccess.h"
|
||||
#include "asm/stat.h"
|
||||
#include "sysdep/syscalls.h"
|
||||
#include "sysdep/sigcontext.h"
|
||||
#include "kern_util.h"
|
||||
#include "syscall.h"
|
||||
|
||||
void syscall_handler_tt(int sig, struct pt_regs *regs)
|
||||
{
|
||||
void *sc;
|
||||
long result;
|
||||
int syscall;
|
||||
|
||||
sc = UPT_SC(®s->regs);
|
||||
SC_START_SYSCALL(sc);
|
||||
|
||||
syscall = UPT_SYSCALL_NR(®s->regs);
|
||||
syscall_trace(®s->regs, 0);
|
||||
|
||||
current->thread.nsyscalls++;
|
||||
nsyscalls++;
|
||||
|
||||
if((syscall >= NR_syscalls) || (syscall < 0))
|
||||
result = -ENOSYS;
|
||||
else result = EXECUTE_SYSCALL(syscall, regs);
|
||||
|
||||
/* regs->sc may have changed while the system call ran (there may
|
||||
* have been an interrupt or segfault), so it needs to be refreshed.
|
||||
*/
|
||||
UPT_SC(®s->regs) = sc;
|
||||
|
||||
SC_SET_SYSCALL_RETURN(sc, result);
|
||||
|
||||
syscall_trace(®s->regs, 1);
|
||||
}
|
||||
61
arch/um/kernel/tt/syscall_user.c
Normal file
61
arch/um/kernel/tt/syscall_user.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <asm/unistd.h>
|
||||
#include "sysdep/ptrace.h"
|
||||
#include "sigcontext.h"
|
||||
#include "ptrace_user.h"
|
||||
#include "task.h"
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "syscall.h"
|
||||
#include "tt.h"
|
||||
|
||||
void do_sigtrap(void *task)
|
||||
{
|
||||
UPT_SYSCALL_NR(TASK_REGS(task)) = -1;
|
||||
}
|
||||
|
||||
void do_syscall(void *task, int pid, int local_using_sysemu)
|
||||
{
|
||||
unsigned long proc_regs[FRAME_SIZE];
|
||||
|
||||
if(ptrace_getregs(pid, proc_regs) < 0)
|
||||
tracer_panic("Couldn't read registers");
|
||||
|
||||
UPT_SYSCALL_NR(TASK_REGS(task)) = PT_SYSCALL_NR(proc_regs);
|
||||
|
||||
#ifdef UPT_ORIGGPR2
|
||||
UPT_ORIGGPR2(TASK_REGS(task)) = REGS_ORIGGPR2(proc_regs);
|
||||
#endif
|
||||
|
||||
if(((unsigned long *) PT_IP(proc_regs) >= &_stext) &&
|
||||
((unsigned long *) PT_IP(proc_regs) <= &_etext))
|
||||
tracer_panic("I'm tracing myself and I can't get out");
|
||||
|
||||
/* advanced sysemu mode set syscall number to -1 automatically */
|
||||
if (local_using_sysemu==2)
|
||||
return;
|
||||
|
||||
/* syscall number -1 in sysemu skips syscall restarting in host */
|
||||
if(ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET,
|
||||
local_using_sysemu ? -1 : __NR_getpid) < 0)
|
||||
tracer_panic("do_syscall : Nullifying syscall failed, "
|
||||
"errno = %d", errno);
|
||||
}
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
121
arch/um/kernel/tt/tlb.c
Normal file
121
arch/um/kernel/tt/tlb.c
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Copyright 2003 PathScale, Inc.
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/stddef.h"
|
||||
#include "linux/kernel.h"
|
||||
#include "linux/sched.h"
|
||||
#include "linux/mm.h"
|
||||
#include "asm/page.h"
|
||||
#include "asm/pgtable.h"
|
||||
#include "asm/uaccess.h"
|
||||
#include "asm/tlbflush.h"
|
||||
#include "user_util.h"
|
||||
#include "mem_user.h"
|
||||
#include "os.h"
|
||||
#include "tlb.h"
|
||||
|
||||
static int do_ops(union mm_context *mmu, struct host_vm_op *ops, int last,
|
||||
int finished, void **flush)
|
||||
{
|
||||
struct host_vm_op *op;
|
||||
int i, ret=0;
|
||||
|
||||
for(i = 0; i <= last && !ret; i++){
|
||||
op = &ops[i];
|
||||
switch(op->type){
|
||||
case MMAP:
|
||||
ret = os_map_memory((void *) op->u.mmap.addr,
|
||||
op->u.mmap.fd, op->u.mmap.offset,
|
||||
op->u.mmap.len, op->u.mmap.r,
|
||||
op->u.mmap.w, op->u.mmap.x);
|
||||
break;
|
||||
case MUNMAP:
|
||||
ret = os_unmap_memory((void *) op->u.munmap.addr,
|
||||
op->u.munmap.len);
|
||||
break;
|
||||
case MPROTECT:
|
||||
ret = protect_memory(op->u.mprotect.addr,
|
||||
op->u.munmap.len,
|
||||
op->u.mprotect.r,
|
||||
op->u.mprotect.w,
|
||||
op->u.mprotect.x, 1);
|
||||
protect_memory(op->u.mprotect.addr, op->u.munmap.len,
|
||||
op->u.mprotect.r, op->u.mprotect.w,
|
||||
op->u.mprotect.x, 1);
|
||||
break;
|
||||
default:
|
||||
printk("Unknown op type %d in do_ops\n", op->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void fix_range(struct mm_struct *mm, unsigned long start_addr,
|
||||
unsigned long end_addr, int force)
|
||||
{
|
||||
if((current->thread.mode.tt.extern_pid != -1) &&
|
||||
(current->thread.mode.tt.extern_pid != os_getpid()))
|
||||
panic("fix_range fixing wrong address space, current = 0x%p",
|
||||
current);
|
||||
|
||||
fix_range_common(mm, start_addr, end_addr, force, do_ops);
|
||||
}
|
||||
|
||||
atomic_t vmchange_seq = ATOMIC_INIT(1);
|
||||
|
||||
void flush_tlb_kernel_range_tt(unsigned long start, unsigned long end)
|
||||
{
|
||||
if(flush_tlb_kernel_range_common(start, end))
|
||||
atomic_inc(&vmchange_seq);
|
||||
}
|
||||
|
||||
void flush_tlb_kernel_vm_tt(void)
|
||||
{
|
||||
flush_tlb_kernel_range(start_vm, end_vm);
|
||||
}
|
||||
|
||||
void __flush_tlb_one_tt(unsigned long addr)
|
||||
{
|
||||
flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
|
||||
}
|
||||
|
||||
void flush_tlb_range_tt(struct vm_area_struct *vma, unsigned long start,
|
||||
unsigned long end)
|
||||
{
|
||||
if(vma->vm_mm != current->mm) return;
|
||||
|
||||
/* Assumes that the range start ... end is entirely within
|
||||
* either process memory or kernel vm
|
||||
*/
|
||||
if((start >= start_vm) && (start < end_vm)){
|
||||
if(flush_tlb_kernel_range_common(start, end))
|
||||
atomic_inc(&vmchange_seq);
|
||||
}
|
||||
else fix_range(vma->vm_mm, start, end, 0);
|
||||
}
|
||||
|
||||
void flush_tlb_mm_tt(struct mm_struct *mm)
|
||||
{
|
||||
unsigned long seq;
|
||||
|
||||
if(mm != current->mm) return;
|
||||
|
||||
fix_range(mm, 0, STACK_TOP, 0);
|
||||
|
||||
seq = atomic_read(&vmchange_seq);
|
||||
if(current->thread.mode.tt.vm_seq == seq)
|
||||
return;
|
||||
current->thread.mode.tt.vm_seq = seq;
|
||||
flush_tlb_kernel_range_common(start_vm, end_vm);
|
||||
}
|
||||
|
||||
void force_flush_all_tt(void)
|
||||
{
|
||||
fix_range(current->mm, 0, STACK_TOP, 1);
|
||||
flush_tlb_kernel_range_common(start_vm, end_vm);
|
||||
}
|
||||
462
arch/um/kernel/tt/tracer.c
Normal file
462
arch/um/kernel/tt/tracer.c
Normal file
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include "user.h"
|
||||
#include "sysdep/ptrace.h"
|
||||
#include "sigcontext.h"
|
||||
#include "sysdep/sigcontext.h"
|
||||
#include "os.h"
|
||||
#include "user_util.h"
|
||||
#include "mem_user.h"
|
||||
#include "process.h"
|
||||
#include "kern_util.h"
|
||||
#include "chan_user.h"
|
||||
#include "ptrace_user.h"
|
||||
#include "irq_user.h"
|
||||
#include "mode.h"
|
||||
#include "tt.h"
|
||||
|
||||
static int tracer_winch[2];
|
||||
|
||||
int is_tracer_winch(int pid, int fd, void *data)
|
||||
{
|
||||
if(pid != os_getpgrp())
|
||||
return(0);
|
||||
|
||||
register_winch_irq(tracer_winch[0], fd, -1, data);
|
||||
return(1);
|
||||
}
|
||||
|
||||
static void tracer_winch_handler(int sig)
|
||||
{
|
||||
int n;
|
||||
char c = 1;
|
||||
|
||||
n = os_write_file(tracer_winch[1], &c, sizeof(c));
|
||||
if(n != sizeof(c))
|
||||
printk("tracer_winch_handler - write failed, err = %d\n", -n);
|
||||
}
|
||||
|
||||
/* Called only by the tracing thread during initialization */
|
||||
|
||||
static void setup_tracer_winch(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = os_pipe(tracer_winch, 1, 1);
|
||||
if(err < 0){
|
||||
printk("setup_tracer_winch : os_pipe failed, err = %d\n", -err);
|
||||
return;
|
||||
}
|
||||
signal(SIGWINCH, tracer_winch_handler);
|
||||
}
|
||||
|
||||
void attach_process(int pid)
|
||||
{
|
||||
if((ptrace(PTRACE_ATTACH, pid, 0, 0) < 0) ||
|
||||
(ptrace(PTRACE_CONT, pid, 0, 0) < 0))
|
||||
tracer_panic("OP_FORK failed to attach pid");
|
||||
wait_for_stop(pid, SIGSTOP, PTRACE_CONT, NULL);
|
||||
if (ptrace(PTRACE_OLDSETOPTIONS, pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0)
|
||||
tracer_panic("OP_FORK: PTRACE_SETOPTIONS failed, errno = %d", errno);
|
||||
if(ptrace(PTRACE_CONT, pid, 0, 0) < 0)
|
||||
tracer_panic("OP_FORK failed to continue process");
|
||||
}
|
||||
|
||||
void tracer_panic(char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
vprintf(format, ap);
|
||||
va_end(ap);
|
||||
printf("\n");
|
||||
while(1) pause();
|
||||
}
|
||||
|
||||
static void tracer_segv(int sig, struct sigcontext sc)
|
||||
{
|
||||
struct faultinfo fi;
|
||||
GET_FAULTINFO_FROM_SC(fi, &sc);
|
||||
printf("Tracing thread segfault at address 0x%lx, ip 0x%lx\n",
|
||||
FAULT_ADDRESS(fi), SC_IP(&sc));
|
||||
while(1)
|
||||
pause();
|
||||
}
|
||||
|
||||
/* Changed early in boot, and then only read */
|
||||
int debug = 0;
|
||||
int debug_stop = 1;
|
||||
int debug_parent = 0;
|
||||
int honeypot = 0;
|
||||
|
||||
static int signal_tramp(void *arg)
|
||||
{
|
||||
int (*proc)(void *);
|
||||
|
||||
if(honeypot && munmap((void *) (host_task_size - 0x10000000),
|
||||
0x10000000))
|
||||
panic("Unmapping stack failed");
|
||||
if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0)
|
||||
panic("ptrace PTRACE_TRACEME failed");
|
||||
os_stop_process(os_getpid());
|
||||
change_sig(SIGWINCH, 0);
|
||||
signal(SIGUSR1, SIG_IGN);
|
||||
change_sig(SIGCHLD, 0);
|
||||
signal(SIGSEGV, (__sighandler_t) sig_handler);
|
||||
set_cmdline("(idle thread)");
|
||||
set_init_pid(os_getpid());
|
||||
init_irq_signals(0);
|
||||
proc = arg;
|
||||
return((*proc)(NULL));
|
||||
}
|
||||
|
||||
static void sleeping_process_signal(int pid, int sig)
|
||||
{
|
||||
switch(sig){
|
||||
/* These two result from UML being ^Z-ed and bg-ed. PTRACE_CONT is
|
||||
* right because the process must be in the kernel already.
|
||||
*/
|
||||
case SIGCONT:
|
||||
case SIGTSTP:
|
||||
if(ptrace(PTRACE_CONT, pid, 0, sig) < 0)
|
||||
tracer_panic("sleeping_process_signal : Failed to "
|
||||
"continue pid %d, signal = %d, "
|
||||
"errno = %d\n", pid, sig, errno);
|
||||
break;
|
||||
|
||||
/* This happens when the debugger (e.g. strace) is doing system call
|
||||
* tracing on the kernel. During a context switch, the current task
|
||||
* will be set to the incoming process and the outgoing process will
|
||||
* hop into write and then read. Since it's not the current process
|
||||
* any more, the trace of those will land here. So, we need to just
|
||||
* PTRACE_SYSCALL it.
|
||||
*/
|
||||
case (SIGTRAP + 0x80):
|
||||
if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
|
||||
tracer_panic("sleeping_process_signal : Failed to "
|
||||
"PTRACE_SYSCALL pid %d, errno = %d\n",
|
||||
pid, errno);
|
||||
break;
|
||||
case SIGSTOP:
|
||||
break;
|
||||
default:
|
||||
tracer_panic("sleeping process %d got unexpected "
|
||||
"signal : %d\n", pid, sig);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Accessed only by the tracing thread */
|
||||
int debugger_pid = -1;
|
||||
int debugger_parent = -1;
|
||||
int debugger_fd = -1;
|
||||
int gdb_pid = -1;
|
||||
|
||||
struct {
|
||||
int pid;
|
||||
int signal;
|
||||
unsigned long addr;
|
||||
struct timeval time;
|
||||
} signal_record[1024][32];
|
||||
|
||||
int signal_index[32];
|
||||
int nsignals = 0;
|
||||
int debug_trace = 0;
|
||||
|
||||
extern void signal_usr1(int sig);
|
||||
|
||||
int tracing_pid = -1;
|
||||
|
||||
int tracer(int (*init_proc)(void *), void *sp)
|
||||
{
|
||||
void *task = NULL;
|
||||
int status, pid = 0, sig = 0, cont_type, tracing = 0, op = 0;
|
||||
int proc_id = 0, n, err, old_tracing = 0, strace = 0;
|
||||
int local_using_sysemu = 0;
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
setup_tracer_winch();
|
||||
tracing_pid = os_getpid();
|
||||
printf("tracing thread pid = %d\n", tracing_pid);
|
||||
|
||||
pid = clone(signal_tramp, sp, CLONE_FILES | SIGCHLD, init_proc);
|
||||
CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
|
||||
if(n < 0){
|
||||
printf("waitpid on idle thread failed, errno = %d\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
if (ptrace(PTRACE_OLDSETOPTIONS, pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0) {
|
||||
printf("Failed to PTRACE_SETOPTIONS for idle thread, errno = %d\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
if((ptrace(PTRACE_CONT, pid, 0, 0) < 0)){
|
||||
printf("Failed to continue idle thread, errno = %d\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
signal(SIGSEGV, (sighandler_t) tracer_segv);
|
||||
signal(SIGUSR1, signal_usr1);
|
||||
if(debug_trace){
|
||||
printf("Tracing thread pausing to be attached\n");
|
||||
stop();
|
||||
}
|
||||
if(debug){
|
||||
if(gdb_pid != -1)
|
||||
debugger_pid = attach_debugger(pid, gdb_pid, 1);
|
||||
else debugger_pid = init_ptrace_proxy(pid, 1, debug_stop);
|
||||
if(debug_parent){
|
||||
debugger_parent = os_process_parent(debugger_pid);
|
||||
init_parent_proxy(debugger_parent);
|
||||
err = attach(debugger_parent);
|
||||
if(err){
|
||||
printf("Failed to attach debugger parent %d, "
|
||||
"errno = %d\n", debugger_parent, -err);
|
||||
debugger_parent = -1;
|
||||
}
|
||||
else {
|
||||
if(ptrace(PTRACE_SYSCALL, debugger_parent,
|
||||
0, 0) < 0){
|
||||
printf("Failed to continue debugger "
|
||||
"parent, errno = %d\n", errno);
|
||||
debugger_parent = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
set_cmdline("(tracing thread)");
|
||||
while(1){
|
||||
CATCH_EINTR(pid = waitpid(-1, &status, WUNTRACED));
|
||||
if(pid <= 0){
|
||||
if(errno != ECHILD){
|
||||
printf("wait failed - errno = %d\n", errno);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(pid == debugger_pid){
|
||||
int cont = 0;
|
||||
|
||||
if(WIFEXITED(status) || WIFSIGNALED(status))
|
||||
debugger_pid = -1;
|
||||
/* XXX Figure out how to deal with gdb and SMP */
|
||||
else cont = debugger_signal(status, cpu_tasks[0].pid);
|
||||
if(cont == PTRACE_SYSCALL) strace = 1;
|
||||
continue;
|
||||
}
|
||||
else if(pid == debugger_parent){
|
||||
debugger_parent_signal(status, pid);
|
||||
continue;
|
||||
}
|
||||
nsignals++;
|
||||
if(WIFEXITED(status)) ;
|
||||
#ifdef notdef
|
||||
{
|
||||
printf("Child %d exited with status %d\n", pid,
|
||||
WEXITSTATUS(status));
|
||||
}
|
||||
#endif
|
||||
else if(WIFSIGNALED(status)){
|
||||
sig = WTERMSIG(status);
|
||||
if(sig != 9){
|
||||
printf("Child %d exited with signal %d\n", pid,
|
||||
sig);
|
||||
}
|
||||
}
|
||||
else if(WIFSTOPPED(status)){
|
||||
proc_id = pid_to_processor_id(pid);
|
||||
sig = WSTOPSIG(status);
|
||||
if(proc_id == -1){
|
||||
sleeping_process_signal(pid, sig);
|
||||
continue;
|
||||
}
|
||||
|
||||
task = cpu_tasks[proc_id].task;
|
||||
tracing = is_tracing(task);
|
||||
old_tracing = tracing;
|
||||
|
||||
/* Assume: no syscall, when coming from user */
|
||||
if ( tracing )
|
||||
do_sigtrap(task);
|
||||
|
||||
switch(sig){
|
||||
case SIGUSR1:
|
||||
sig = 0;
|
||||
op = do_proc_op(task, proc_id);
|
||||
switch(op){
|
||||
/*
|
||||
* This is called when entering user mode; after
|
||||
* this, we start intercepting syscalls.
|
||||
*
|
||||
* In fact, a process is started in kernel mode,
|
||||
* so with is_tracing() == 0 (and that is reset
|
||||
* when executing syscalls, since UML kernel has
|
||||
* the right to do syscalls);
|
||||
*/
|
||||
case OP_TRACE_ON:
|
||||
arch_leave_kernel(task, pid);
|
||||
tracing = 1;
|
||||
break;
|
||||
case OP_REBOOT:
|
||||
case OP_HALT:
|
||||
unmap_physmem();
|
||||
kmalloc_ok = 0;
|
||||
os_kill_ptraced_process(pid, 0);
|
||||
/* Now let's reap remaining zombies */
|
||||
errno = 0;
|
||||
do {
|
||||
waitpid(-1, &status,
|
||||
WUNTRACED);
|
||||
} while (errno != ECHILD);
|
||||
return(op == OP_REBOOT);
|
||||
case OP_NONE:
|
||||
printf("Detaching pid %d\n", pid);
|
||||
detach(pid, SIGSTOP);
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* OP_EXEC switches host processes on us,
|
||||
* we want to continue the new one.
|
||||
*/
|
||||
pid = cpu_tasks[proc_id].pid;
|
||||
break;
|
||||
case (SIGTRAP + 0x80):
|
||||
if(!tracing && (debugger_pid != -1)){
|
||||
child_signal(pid, status & 0x7fff);
|
||||
continue;
|
||||
}
|
||||
tracing = 0;
|
||||
/* local_using_sysemu has been already set
|
||||
* below, since if we are here, is_tracing() on
|
||||
* the traced task was 1, i.e. the process had
|
||||
* already run through one iteration of the
|
||||
* loop which executed a OP_TRACE_ON request.*/
|
||||
do_syscall(task, pid, local_using_sysemu);
|
||||
sig = SIGUSR2;
|
||||
break;
|
||||
case SIGTRAP:
|
||||
if(!tracing && (debugger_pid != -1)){
|
||||
child_signal(pid, status);
|
||||
continue;
|
||||
}
|
||||
tracing = 0;
|
||||
break;
|
||||
case SIGPROF:
|
||||
if(tracing) sig = 0;
|
||||
break;
|
||||
case SIGCHLD:
|
||||
case SIGHUP:
|
||||
sig = 0;
|
||||
break;
|
||||
case SIGSEGV:
|
||||
case SIGIO:
|
||||
case SIGALRM:
|
||||
case SIGVTALRM:
|
||||
case SIGFPE:
|
||||
case SIGBUS:
|
||||
case SIGILL:
|
||||
case SIGWINCH:
|
||||
|
||||
default:
|
||||
tracing = 0;
|
||||
break;
|
||||
}
|
||||
set_tracing(task, tracing);
|
||||
|
||||
if(!tracing && old_tracing)
|
||||
arch_enter_kernel(task, pid);
|
||||
|
||||
if(!tracing && (debugger_pid != -1) && (sig != 0) &&
|
||||
(sig != SIGALRM) && (sig != SIGVTALRM) &&
|
||||
(sig != SIGSEGV) && (sig != SIGTRAP) &&
|
||||
(sig != SIGUSR2) && (sig != SIGIO) &&
|
||||
(sig != SIGFPE)){
|
||||
child_signal(pid, status);
|
||||
continue;
|
||||
}
|
||||
|
||||
local_using_sysemu = get_using_sysemu();
|
||||
|
||||
if(tracing)
|
||||
cont_type = SELECT_PTRACE_OPERATION(local_using_sysemu,
|
||||
singlestepping(task));
|
||||
else if((debugger_pid != -1) && strace)
|
||||
cont_type = PTRACE_SYSCALL;
|
||||
else
|
||||
cont_type = PTRACE_CONT;
|
||||
|
||||
if(ptrace(cont_type, pid, 0, sig) != 0){
|
||||
tracer_panic("ptrace failed to continue "
|
||||
"process - errno = %d\n",
|
||||
errno);
|
||||
}
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
static int __init uml_debug_setup(char *line, int *add)
|
||||
{
|
||||
char *next;
|
||||
|
||||
debug = 1;
|
||||
*add = 0;
|
||||
if(*line != '=') return(0);
|
||||
line++;
|
||||
|
||||
while(line != NULL){
|
||||
next = strchr(line, ',');
|
||||
if(next) *next++ = '\0';
|
||||
|
||||
if(!strcmp(line, "go")) debug_stop = 0;
|
||||
else if(!strcmp(line, "parent")) debug_parent = 1;
|
||||
else printf("Unknown debug option : '%s'\n", line);
|
||||
|
||||
line = next;
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
__uml_setup("debug", uml_debug_setup,
|
||||
"debug\n"
|
||||
" Starts up the kernel under the control of gdb. See the \n"
|
||||
" kernel debugging tutorial and the debugging session pages\n"
|
||||
" at http://user-mode-linux.sourceforge.net/ for more information.\n\n"
|
||||
);
|
||||
|
||||
static int __init uml_debugtrace_setup(char *line, int *add)
|
||||
{
|
||||
debug_trace = 1;
|
||||
return 0;
|
||||
}
|
||||
__uml_setup("debugtrace", uml_debugtrace_setup,
|
||||
"debugtrace\n"
|
||||
" Causes the tracing thread to pause until it is attached by a\n"
|
||||
" debugger and continued. This is mostly for debugging crashes\n"
|
||||
" early during boot, and should be pretty much obsoleted by\n"
|
||||
" the debug switch.\n\n"
|
||||
);
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
71
arch/um/kernel/tt/trap_user.c
Normal file
71
arch/um/kernel/tt/trap_user.c
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include "sysdep/ptrace.h"
|
||||
#include "sysdep/sigcontext.h"
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "task.h"
|
||||
#include "tt.h"
|
||||
#include "os.h"
|
||||
|
||||
void sig_handler_common_tt(int sig, void *sc_ptr)
|
||||
{
|
||||
struct sigcontext *sc = sc_ptr;
|
||||
struct tt_regs save_regs, *r;
|
||||
int save_errno = errno, is_user = 0;
|
||||
void (*handler)(int, union uml_pt_regs *);
|
||||
|
||||
/* This is done because to allow SIGSEGV to be delivered inside a SEGV
|
||||
* handler. This can happen in copy_user, and if SEGV is disabled,
|
||||
* the process will die.
|
||||
*/
|
||||
if(sig == SIGSEGV)
|
||||
change_sig(SIGSEGV, 1);
|
||||
|
||||
r = &TASK_REGS(get_current())->tt;
|
||||
if ( sig == SIGFPE || sig == SIGSEGV ||
|
||||
sig == SIGBUS || sig == SIGILL ||
|
||||
sig == SIGTRAP ) {
|
||||
GET_FAULTINFO_FROM_SC(r->faultinfo, sc);
|
||||
}
|
||||
save_regs = *r;
|
||||
if (sc)
|
||||
is_user = user_context(SC_SP(sc));
|
||||
r->sc = sc;
|
||||
if(sig != SIGUSR2)
|
||||
r->syscall = -1;
|
||||
|
||||
handler = sig_info[sig];
|
||||
|
||||
/* unblock SIGALRM, SIGVTALRM, SIGIO if sig isn't IRQ signal */
|
||||
if (sig != SIGIO && sig != SIGWINCH &&
|
||||
sig != SIGVTALRM && sig != SIGALRM)
|
||||
unblock_signals();
|
||||
|
||||
handler(sig, (union uml_pt_regs *) r);
|
||||
|
||||
if(is_user){
|
||||
interrupt_end();
|
||||
block_signals();
|
||||
set_user_mode(NULL);
|
||||
}
|
||||
*r = save_regs;
|
||||
errno = save_errno;
|
||||
}
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
73
arch/um/kernel/tt/uaccess.c
Normal file
73
arch/um/kernel/tt/uaccess.c
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/sched.h"
|
||||
#include "asm/uaccess.h"
|
||||
|
||||
int copy_from_user_tt(void *to, const void __user *from, int n)
|
||||
{
|
||||
if(!access_ok(VERIFY_READ, from, n))
|
||||
return(n);
|
||||
|
||||
return(__do_copy_from_user(to, from, n, ¤t->thread.fault_addr,
|
||||
¤t->thread.fault_catcher));
|
||||
}
|
||||
|
||||
int copy_to_user_tt(void __user *to, const void *from, int n)
|
||||
{
|
||||
if(!access_ok(VERIFY_WRITE, to, n))
|
||||
return(n);
|
||||
|
||||
return(__do_copy_to_user(to, from, n, ¤t->thread.fault_addr,
|
||||
¤t->thread.fault_catcher));
|
||||
}
|
||||
|
||||
int strncpy_from_user_tt(char *dst, const char __user *src, int count)
|
||||
{
|
||||
int n;
|
||||
|
||||
if(!access_ok(VERIFY_READ, src, 1))
|
||||
return(-EFAULT);
|
||||
|
||||
n = __do_strncpy_from_user(dst, src, count,
|
||||
¤t->thread.fault_addr,
|
||||
¤t->thread.fault_catcher);
|
||||
if(n < 0) return(-EFAULT);
|
||||
return(n);
|
||||
}
|
||||
|
||||
int __clear_user_tt(void __user *mem, int len)
|
||||
{
|
||||
return(__do_clear_user(mem, len,
|
||||
¤t->thread.fault_addr,
|
||||
¤t->thread.fault_catcher));
|
||||
}
|
||||
|
||||
int clear_user_tt(void __user *mem, int len)
|
||||
{
|
||||
if(!access_ok(VERIFY_WRITE, mem, len))
|
||||
return(len);
|
||||
|
||||
return(__do_clear_user(mem, len, ¤t->thread.fault_addr,
|
||||
¤t->thread.fault_catcher));
|
||||
}
|
||||
|
||||
int strnlen_user_tt(const void __user *str, int len)
|
||||
{
|
||||
return(__do_strnlen_user(str, len,
|
||||
¤t->thread.fault_addr,
|
||||
¤t->thread.fault_catcher));
|
||||
}
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
106
arch/um/kernel/tt/uaccess_user.c
Normal file
106
arch/um/kernel/tt/uaccess_user.c
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Chris Emerson (cemerson@chiark.greenend.org.uk)
|
||||
* Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "user_util.h"
|
||||
#include "uml_uaccess.h"
|
||||
#include "task.h"
|
||||
#include "kern_util.h"
|
||||
#include "os.h"
|
||||
#include "longjmp.h"
|
||||
|
||||
int __do_copy_from_user(void *to, const void *from, int n,
|
||||
void **fault_addr, void **fault_catcher)
|
||||
{
|
||||
struct tt_regs save = TASK_REGS(get_current())->tt;
|
||||
unsigned long fault;
|
||||
int faulted;
|
||||
|
||||
fault = __do_user_copy(to, from, n, fault_addr, fault_catcher,
|
||||
__do_copy, &faulted);
|
||||
TASK_REGS(get_current())->tt = save;
|
||||
|
||||
if(!faulted)
|
||||
return 0;
|
||||
else if (fault)
|
||||
return n - (fault - (unsigned long) from);
|
||||
else
|
||||
/* In case of a general protection fault, we don't have the
|
||||
* fault address, so NULL is used instead. Pretend we didn't
|
||||
* copy anything. */
|
||||
return n;
|
||||
}
|
||||
|
||||
static void __do_strncpy(void *dst, const void *src, int count)
|
||||
{
|
||||
strncpy(dst, src, count);
|
||||
}
|
||||
|
||||
int __do_strncpy_from_user(char *dst, const char *src, unsigned long count,
|
||||
void **fault_addr, void **fault_catcher)
|
||||
{
|
||||
struct tt_regs save = TASK_REGS(get_current())->tt;
|
||||
unsigned long fault;
|
||||
int faulted;
|
||||
|
||||
fault = __do_user_copy(dst, src, count, fault_addr, fault_catcher,
|
||||
__do_strncpy, &faulted);
|
||||
TASK_REGS(get_current())->tt = save;
|
||||
|
||||
if(!faulted) return(strlen(dst));
|
||||
else return(-1);
|
||||
}
|
||||
|
||||
static void __do_clear(void *to, const void *from, int n)
|
||||
{
|
||||
memset(to, 0, n);
|
||||
}
|
||||
|
||||
int __do_clear_user(void *mem, unsigned long len,
|
||||
void **fault_addr, void **fault_catcher)
|
||||
{
|
||||
struct tt_regs save = TASK_REGS(get_current())->tt;
|
||||
unsigned long fault;
|
||||
int faulted;
|
||||
|
||||
fault = __do_user_copy(mem, NULL, len, fault_addr, fault_catcher,
|
||||
__do_clear, &faulted);
|
||||
TASK_REGS(get_current())->tt = save;
|
||||
|
||||
if(!faulted) return(0);
|
||||
else return(len - (fault - (unsigned long) mem));
|
||||
}
|
||||
|
||||
int __do_strnlen_user(const char *str, unsigned long n,
|
||||
void **fault_addr, void **fault_catcher)
|
||||
{
|
||||
struct tt_regs save = TASK_REGS(get_current())->tt;
|
||||
int ret;
|
||||
unsigned long *faddrp = (unsigned long *)fault_addr;
|
||||
jmp_buf jbuf;
|
||||
|
||||
*fault_catcher = &jbuf;
|
||||
if(UML_SETJMP(&jbuf) == 0)
|
||||
ret = strlen(str) + 1;
|
||||
else ret = *faddrp - (unsigned long) str;
|
||||
|
||||
*fault_addr = NULL;
|
||||
*fault_catcher = NULL;
|
||||
|
||||
TASK_REGS(get_current())->tt = save;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
30
arch/um/kernel/uaccess.c
Normal file
30
arch/um/kernel/uaccess.c
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Chris Emerson (cemerson@chiark.greenend.org.uk)
|
||||
* Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
/* These are here rather than tt/uaccess.c because skas mode needs them in
|
||||
* order to do SIGBUS recovery when a tmpfs mount runs out of room.
|
||||
*/
|
||||
|
||||
#include <linux/string.h>
|
||||
#include "os.h"
|
||||
|
||||
void __do_copy(void *to, const void *from, int n)
|
||||
{
|
||||
memcpy(to, from, n);
|
||||
}
|
||||
|
||||
|
||||
int __do_copy_to_user(void *to, const void *from, int n,
|
||||
void **fault_addr, void **fault_catcher)
|
||||
{
|
||||
unsigned long fault;
|
||||
int faulted;
|
||||
|
||||
fault = __do_user_copy(to, from, n, fault_addr, fault_catcher,
|
||||
__do_copy, &faulted);
|
||||
if(!faulted) return(0);
|
||||
else return(n - (fault - (unsigned long) to));
|
||||
}
|
||||
509
arch/um/kernel/um_arch.c
Normal file
509
arch/um/kernel/um_arch.c
Normal file
@@ -0,0 +1,509 @@
|
||||
/*
|
||||
* Copyright (C) 2000, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/kernel.h"
|
||||
#include "linux/sched.h"
|
||||
#include "linux/notifier.h"
|
||||
#include "linux/mm.h"
|
||||
#include "linux/types.h"
|
||||
#include "linux/tty.h"
|
||||
#include "linux/init.h"
|
||||
#include "linux/bootmem.h"
|
||||
#include "linux/spinlock.h"
|
||||
#include "linux/utsname.h"
|
||||
#include "linux/sysrq.h"
|
||||
#include "linux/seq_file.h"
|
||||
#include "linux/delay.h"
|
||||
#include "linux/module.h"
|
||||
#include "asm/page.h"
|
||||
#include "asm/pgtable.h"
|
||||
#include "asm/ptrace.h"
|
||||
#include "asm/elf.h"
|
||||
#include "asm/user.h"
|
||||
#include "asm/setup.h"
|
||||
#include "ubd_user.h"
|
||||
#include "asm/current.h"
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "kern.h"
|
||||
#include "mem_user.h"
|
||||
#include "mem.h"
|
||||
#include "initrd.h"
|
||||
#include "init.h"
|
||||
#include "os.h"
|
||||
#include "choose-mode.h"
|
||||
#include "mode_kern.h"
|
||||
#include "mode.h"
|
||||
#ifdef UML_CONFIG_MODE_SKAS
|
||||
#include "skas.h"
|
||||
#endif
|
||||
|
||||
#define DEFAULT_COMMAND_LINE "root=98:0"
|
||||
|
||||
/* Changed in linux_main 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) {
|
||||
printf("add_arg: Too many command line arguments!\n");
|
||||
exit(1);
|
||||
}
|
||||
if(strlen(command_line) > 0)
|
||||
strcat(command_line, " ");
|
||||
strcat(command_line, arg);
|
||||
}
|
||||
|
||||
struct cpuinfo_um boot_cpu_data = {
|
||||
.loops_per_jiffy = 0,
|
||||
.ipi_pipe = { -1, -1 }
|
||||
};
|
||||
|
||||
unsigned long thread_saved_pc(struct task_struct *task)
|
||||
{
|
||||
return(os_process_pc(CHOOSE_MODE_PROC(thread_pid_tt, thread_pid_skas,
|
||||
task)));
|
||||
}
|
||||
|
||||
static int show_cpuinfo(struct seq_file *m, void *v)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
index = (struct cpuinfo_um *) v - cpu_data;
|
||||
if (!cpu_online(index))
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
seq_printf(m, "processor\t: %d\n", index);
|
||||
seq_printf(m, "vendor_id\t: User Mode Linux\n");
|
||||
seq_printf(m, "model name\t: UML\n");
|
||||
seq_printf(m, "mode\t\t: %s\n", CHOOSE_MODE("tt", "skas"));
|
||||
seq_printf(m, "host\t\t: %s\n", host_info);
|
||||
seq_printf(m, "bogomips\t: %lu.%02lu\n\n",
|
||||
loops_per_jiffy/(500000/HZ),
|
||||
(loops_per_jiffy/(5000/HZ)) % 100);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
static void *c_start(struct seq_file *m, loff_t *pos)
|
||||
{
|
||||
return *pos < NR_CPUS ? cpu_data + *pos : NULL;
|
||||
}
|
||||
|
||||
static void *c_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
{
|
||||
++*pos;
|
||||
return c_start(m, pos);
|
||||
}
|
||||
|
||||
static void c_stop(struct seq_file *m, void *v)
|
||||
{
|
||||
}
|
||||
|
||||
const struct seq_operations cpuinfo_op = {
|
||||
.start = c_start,
|
||||
.next = c_next,
|
||||
.stop = c_stop,
|
||||
.show = show_cpuinfo,
|
||||
};
|
||||
|
||||
/* Set in linux_main */
|
||||
unsigned long host_task_size;
|
||||
unsigned long task_size;
|
||||
|
||||
unsigned long uml_start;
|
||||
|
||||
/* Set in early boot */
|
||||
unsigned long uml_physmem;
|
||||
unsigned long uml_reserved;
|
||||
unsigned long start_vm;
|
||||
unsigned long end_vm;
|
||||
int ncpus = 1;
|
||||
|
||||
#ifdef CONFIG_CMDLINE_ON_HOST
|
||||
/* Pointer set in linux_main, the array itself is private to each thread,
|
||||
* and changed at address space creation time so this poses no concurrency
|
||||
* problems.
|
||||
*/
|
||||
static char *argv1_begin = NULL;
|
||||
static char *argv1_end = NULL;
|
||||
#endif
|
||||
|
||||
/* Set in early boot */
|
||||
static int have_root __initdata = 0;
|
||||
long long physmem_size = 32 * 1024 * 1024;
|
||||
|
||||
void set_cmdline(char *cmd)
|
||||
{
|
||||
#ifdef CONFIG_CMDLINE_ON_HOST
|
||||
char *umid, *ptr;
|
||||
|
||||
if(CHOOSE_MODE(honeypot, 0)) return;
|
||||
|
||||
umid = get_umid();
|
||||
if(*umid != '\0'){
|
||||
snprintf(argv1_begin,
|
||||
(argv1_end - argv1_begin) * sizeof(*ptr),
|
||||
"(%s) ", umid);
|
||||
ptr = &argv1_begin[strlen(argv1_begin)];
|
||||
}
|
||||
else ptr = argv1_begin;
|
||||
|
||||
snprintf(ptr, (argv1_end - ptr) * sizeof(*ptr), "[%s]", cmd);
|
||||
memset(argv1_begin + strlen(argv1_begin), '\0',
|
||||
argv1_end - argv1_begin - strlen(argv1_begin));
|
||||
#endif
|
||||
}
|
||||
|
||||
static 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)
|
||||
{
|
||||
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"
|
||||
);
|
||||
|
||||
#ifndef CONFIG_MODE_TT
|
||||
|
||||
static int __init no_skas_debug_setup(char *line, int *add)
|
||||
{
|
||||
printf("'debug' is not necessary to gdb UML in skas mode - run \n");
|
||||
printf("'gdb linux' and disable CONFIG_CMDLINE_ON_HOST if gdb \n");
|
||||
printf("doesn't work as expected\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__uml_setup("debug", no_skas_debug_setup,
|
||||
"debug\n"
|
||||
" this flag is not needed to run gdb on UML in skas mode\n\n"
|
||||
);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static int __init uml_ncpus_setup(char *line, int *add)
|
||||
{
|
||||
if (!sscanf(line, "%d", &ncpus)) {
|
||||
printf("Couldn't parse [%s]\n", line);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__uml_setup("ncpus=", uml_ncpus_setup,
|
||||
"ncpus=<# of desired CPUs>\n"
|
||||
" This tells an SMP kernel how many virtual processors to start.\n\n"
|
||||
);
|
||||
#endif
|
||||
|
||||
static int force_tt = 0;
|
||||
|
||||
#if defined(CONFIG_MODE_TT) && defined(CONFIG_MODE_SKAS)
|
||||
#define DEFAULT_TT 0
|
||||
|
||||
static int __init mode_tt_setup(char *line, int *add)
|
||||
{
|
||||
force_tt = 1;
|
||||
return(0);
|
||||
}
|
||||
|
||||
#else
|
||||
#ifdef CONFIG_MODE_SKAS
|
||||
|
||||
#define DEFAULT_TT 0
|
||||
|
||||
static int __init mode_tt_setup(char *line, int *add)
|
||||
{
|
||||
printf("CONFIG_MODE_TT disabled - 'mode=tt' ignored\n");
|
||||
return(0);
|
||||
}
|
||||
|
||||
#else
|
||||
#ifdef CONFIG_MODE_TT
|
||||
|
||||
#define DEFAULT_TT 1
|
||||
|
||||
static int __init mode_tt_setup(char *line, int *add)
|
||||
{
|
||||
printf("CONFIG_MODE_SKAS disabled - 'mode=tt' redundant\n");
|
||||
return(0);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
__uml_setup("mode=tt", mode_tt_setup,
|
||||
"mode=tt\n"
|
||||
" When both CONFIG_MODE_TT and CONFIG_MODE_SKAS are enabled, this option\n"
|
||||
" forces UML to run in tt (tracing thread) mode. It is not the default\n"
|
||||
" because it's slower and less secure than skas mode.\n\n"
|
||||
);
|
||||
|
||||
int mode_tt = DEFAULT_TT;
|
||||
|
||||
static int __init Usage(char *line, int *add)
|
||||
{
|
||||
const char **p;
|
||||
|
||||
printf(usage_string, init_utsname()->release);
|
||||
p = &__uml_help_start;
|
||||
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 int __init uml_checksetup(char *line, int *add)
|
||||
{
|
||||
struct uml_param *p;
|
||||
|
||||
p = &__uml_setup_start;
|
||||
while(p < &__uml_setup_end) {
|
||||
int n;
|
||||
|
||||
n = strlen(p->str);
|
||||
if(!strncmp(line, p->str, n)){
|
||||
if (p->setup_func(line + n, add)) return 1;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init uml_postsetup(void)
|
||||
{
|
||||
initcall_t *p;
|
||||
|
||||
p = &__uml_postsetup_start;
|
||||
while(p < &__uml_postsetup_end){
|
||||
(*p)();
|
||||
p++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set during early boot */
|
||||
unsigned long brk_start;
|
||||
unsigned long end_iomem;
|
||||
EXPORT_SYMBOL(end_iomem);
|
||||
|
||||
#define MIN_VMALLOC (32 * 1024 * 1024)
|
||||
|
||||
extern char __binary_start;
|
||||
|
||||
int __init linux_main(int argc, char **argv)
|
||||
{
|
||||
unsigned long avail, diff;
|
||||
unsigned long virtmem_size, max_physmem;
|
||||
unsigned int i, add;
|
||||
char * mode;
|
||||
|
||||
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);
|
||||
|
||||
os_early_checks();
|
||||
if (force_tt)
|
||||
clear_can_do_skas();
|
||||
mode_tt = force_tt ? 1 : !can_do_skas();
|
||||
#ifndef CONFIG_MODE_TT
|
||||
if (mode_tt) {
|
||||
/*Since CONFIG_MODE_TT is #undef'ed, force_tt cannot be 1. So,
|
||||
* can_do_skas() returned 0, and the message is correct. */
|
||||
printf("Support for TT mode is disabled, and no SKAS support is present on the host.\n");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_MODE_SKAS
|
||||
mode = "TT";
|
||||
#else
|
||||
/* Show to the user the result of selection */
|
||||
if (mode_tt)
|
||||
mode = "TT";
|
||||
else if (proc_mm && ptrace_faultinfo)
|
||||
mode = "SKAS3";
|
||||
else
|
||||
mode = "SKAS0";
|
||||
#endif
|
||||
|
||||
printf("UML running in %s mode\n", mode);
|
||||
|
||||
uml_start = (unsigned long) &__binary_start;
|
||||
host_task_size = CHOOSE_MODE_PROC(set_task_sizes_tt,
|
||||
set_task_sizes_skas, &task_size);
|
||||
|
||||
/*
|
||||
* Setting up handlers to 'sig_info' struct
|
||||
*/
|
||||
os_fill_handlinfo(handlinfo_kern);
|
||||
|
||||
brk_start = (unsigned long) sbrk(0);
|
||||
CHOOSE_MODE_PROC(before_mem_tt, before_mem_skas, brk_start);
|
||||
/* 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 = UML_ROUND_UP(brk_start) - UML_ROUND_UP(&_end);
|
||||
if(diff > 1024 * 1024){
|
||||
printf("Adding %ld bytes to physical memory to account for "
|
||||
"exec-shield gap\n", diff);
|
||||
physmem_size += UML_ROUND_UP(brk_start) - UML_ROUND_UP(&_end);
|
||||
}
|
||||
|
||||
uml_physmem = uml_start & PAGE_MASK;
|
||||
|
||||
/* Reserve up to 4M after the current brk */
|
||||
uml_reserved = ROUND_4M(brk_start) + (1 << 22);
|
||||
|
||||
setup_machinename(init_utsname()->machine);
|
||||
|
||||
#ifdef CONFIG_CMDLINE_ON_HOST
|
||||
argv1_begin = argv[1];
|
||||
argv1_end = &argv[1][strlen(argv[1])];
|
||||
#endif
|
||||
|
||||
highmem = 0;
|
||||
iomem_size = (iomem_size + PAGE_SIZE - 1) & PAGE_MASK;
|
||||
max_physmem = get_kmem_end() - uml_physmem - iomem_size - MIN_VMALLOC;
|
||||
|
||||
/* Zones have to begin on a 1 << MAX_ORDER page boundary,
|
||||
* so this makes sure that's true for highmem
|
||||
*/
|
||||
max_physmem &= ~((1 << (PAGE_SHIFT + MAX_ORDER)) - 1);
|
||||
if(physmem_size + iomem_size > max_physmem){
|
||||
highmem = physmem_size + iomem_size - max_physmem;
|
||||
physmem_size -= highmem;
|
||||
#ifndef CONFIG_HIGHMEM
|
||||
highmem = 0;
|
||||
printf("CONFIG_HIGHMEM not enabled - physical memory shrunk "
|
||||
"to %Lu bytes\n", physmem_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
high_physmem = uml_physmem + physmem_size;
|
||||
end_iomem = high_physmem + iomem_size;
|
||||
high_memory = (void *) end_iomem;
|
||||
|
||||
start_vm = VMALLOC_START;
|
||||
|
||||
setup_physmem(uml_physmem, uml_reserved, physmem_size, highmem);
|
||||
if(init_maps(physmem_size, iomem_size, highmem)){
|
||||
printf("Failed to allocate mem_map for %Lu bytes of physical "
|
||||
"memory and %Lu bytes of highmem\n", physmem_size,
|
||||
highmem);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
virtmem_size = physmem_size;
|
||||
avail = get_kmem_end() - start_vm;
|
||||
if(physmem_size > avail) virtmem_size = avail;
|
||||
end_vm = start_vm + virtmem_size;
|
||||
|
||||
if(virtmem_size < physmem_size)
|
||||
printf("Kernel virtual memory size shrunk to %lu bytes\n",
|
||||
virtmem_size);
|
||||
|
||||
uml_postsetup();
|
||||
|
||||
task_protections((unsigned long) &init_thread_info);
|
||||
os_flush_stdout();
|
||||
|
||||
return(CHOOSE_MODE(start_uml_tt(), start_uml_skas()));
|
||||
}
|
||||
|
||||
extern int uml_exitcode;
|
||||
|
||||
static int panic_exit(struct notifier_block *self, unsigned long unused1,
|
||||
void *unused2)
|
||||
{
|
||||
bust_spinlocks(1);
|
||||
show_regs(&(current->thread.regs));
|
||||
bust_spinlocks(0);
|
||||
uml_exitcode = 1;
|
||||
machine_halt();
|
||||
return(0);
|
||||
}
|
||||
|
||||
static struct notifier_block panic_exit_notifier = {
|
||||
.notifier_call = panic_exit,
|
||||
.next = NULL,
|
||||
.priority = 0
|
||||
};
|
||||
|
||||
void __init setup_arch(char **cmdline_p)
|
||||
{
|
||||
atomic_notifier_chain_register(&panic_notifier_list,
|
||||
&panic_exit_notifier);
|
||||
paging_init();
|
||||
strlcpy(boot_command_line, command_line, COMMAND_LINE_SIZE);
|
||||
*cmdline_p = command_line;
|
||||
setup_hostinfo();
|
||||
}
|
||||
|
||||
void __init check_bugs(void)
|
||||
{
|
||||
arch_check_bugs();
|
||||
os_check_bugs();
|
||||
}
|
||||
|
||||
void apply_alternatives(struct alt_instr *start, struct alt_instr *end)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef 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
|
||||
39
arch/um/kernel/umid.c
Normal file
39
arch/um/kernel/umid.c
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "asm/errno.h"
|
||||
#include "init.h"
|
||||
#include "os.h"
|
||||
#include "kern.h"
|
||||
#include "linux/kernel.h"
|
||||
|
||||
/* Changed by set_umid_arg */
|
||||
static int umid_inited = 0;
|
||||
|
||||
static int __init set_umid_arg(char *name, int *add)
|
||||
{
|
||||
int err;
|
||||
|
||||
if(umid_inited){
|
||||
printf("umid already set\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
*add = 0;
|
||||
err = set_umid(name);
|
||||
if(err == -EEXIST)
|
||||
printf("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"
|
||||
);
|
||||
|
||||
106
arch/um/kernel/uml.lds.S
Normal file
106
arch/um/kernel/uml.lds.S
Normal file
@@ -0,0 +1,106 @@
|
||||
#include <asm-generic/vmlinux.lds.h>
|
||||
|
||||
OUTPUT_FORMAT(ELF_FORMAT)
|
||||
OUTPUT_ARCH(ELF_ARCH)
|
||||
ENTRY(_start)
|
||||
jiffies = jiffies_64;
|
||||
|
||||
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;
|
||||
|
||||
#ifdef MODE_TT
|
||||
.remap_data : { UNMAP_PATH (.data .bss) }
|
||||
.remap : { UNMAP_PATH (.text) }
|
||||
|
||||
. = ALIGN(4096); /* Init code and data */
|
||||
#endif
|
||||
|
||||
_text = .;
|
||||
_stext = .;
|
||||
__init_begin = .;
|
||||
.init.text : {
|
||||
_sinittext = .;
|
||||
*(.init.text)
|
||||
_einittext = .;
|
||||
}
|
||||
. = ALIGN(4096);
|
||||
|
||||
.text :
|
||||
{
|
||||
*(.text)
|
||||
SCHED_TEXT
|
||||
LOCK_TEXT
|
||||
*(.fixup)
|
||||
/* .gnu.warning sections are handled specially by elf32.em. */
|
||||
*(.gnu.warning)
|
||||
*(.gnu.linkonce.t*)
|
||||
|
||||
. = ALIGN(4096);
|
||||
__syscall_stub_start = .;
|
||||
*(.__syscall_stub*)
|
||||
__syscall_stub_end = .;
|
||||
. = ALIGN(4096);
|
||||
}
|
||||
|
||||
#include "asm/common.lds.S"
|
||||
|
||||
init.data : { *(init.data) }
|
||||
.data :
|
||||
{
|
||||
. = ALIGN(KERNEL_STACK_SIZE); /* init_task */
|
||||
*(.data.init_task)
|
||||
*(.data)
|
||||
*(.gnu.linkonce.d*)
|
||||
CONSTRUCTORS
|
||||
}
|
||||
.data1 : { *(.data1) }
|
||||
.ctors :
|
||||
{
|
||||
*(.ctors)
|
||||
}
|
||||
.dtors :
|
||||
{
|
||||
*(.dtors)
|
||||
}
|
||||
|
||||
.got : { *(.got.plt) *(.got) }
|
||||
.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(0x1000);
|
||||
.sbss :
|
||||
{
|
||||
__bss_start = .;
|
||||
PROVIDE(_bss_start = .);
|
||||
*(.sbss)
|
||||
*(.scommon)
|
||||
}
|
||||
.bss :
|
||||
{
|
||||
*(.dynbss)
|
||||
*(.bss)
|
||||
*(COMMON)
|
||||
}
|
||||
_end = .;
|
||||
PROVIDE (end = .);
|
||||
|
||||
STABS_DEBUG
|
||||
|
||||
DWARF_DEBUG
|
||||
}
|
||||
5
arch/um/kernel/vmlinux.lds.S
Normal file
5
arch/um/kernel/vmlinux.lds.S
Normal file
@@ -0,0 +1,5 @@
|
||||
#ifdef CONFIG_LD_SCRIPT_STATIC
|
||||
#include "uml.lds.S"
|
||||
#else
|
||||
#include "dyn.lds.S"
|
||||
#endif
|
||||
Reference in New Issue
Block a user