Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
这个提交包含在:
Linus Torvalds
2005-04-16 15:20:36 -07:00
当前提交 1da177e4c3
修改 17291 个文件,包含 6718755 行新增0 行删除

58
arch/um/kernel/Makefile 普通文件
查看文件

@@ -0,0 +1,58 @@
#
# Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
# Licensed under the GPL
#
extra-y := vmlinux.lds
clean-files := vmlinux.lds.S config.tmp
obj-y = checksum.o config.o exec_kern.o exitcode.o \
helper.o init_task.o irq.o irq_user.o ksyms.o main.o mem.o mem_user.o \
physmem.o process.o process_kern.o ptrace.o reboot.o resource.o \
sigio_user.o sigio_kern.o signal_kern.o signal_user.o smp.o \
syscall_kern.o sysrq.o sys_call_table.o tempfile.o time.o time_kern.o \
tlb.o trap_kern.o trap_user.o uaccess_user.o um_arch.o umid.o \
user_util.o
obj-$(CONFIG_BLK_DEV_INITRD) += initrd_kern.o initrd_user.o
obj-$(CONFIG_GPROF) += gprof_syms.o
obj-$(CONFIG_GCOV) += gmon_syms.o
obj-$(CONFIG_TTY_LOG) += tty_log.o
obj-$(CONFIG_SYSCALL_DEBUG) += syscall_user.o
obj-$(CONFIG_MODE_TT) += tt/
obj-$(CONFIG_MODE_SKAS) += skas/
# This needs be compiled with frame pointers regardless of how the rest of the
# kernel is built.
CFLAGS_frame.o := -fno-omit-frame-pointer
user-objs-$(CONFIG_TTY_LOG) += tty_log.o
USER_OBJS := $(user-objs-y) config.o helper.o main.o process.o tempfile.o \
time.o tty_log.o umid.o user_util.o frame.o
include arch/um/scripts/Makefile.rules
targets += config.c
# Be careful with the below Sed code - sed is pitfall-rich!
# We use sed to lower build requirements, for "embedded" builders for instance.
$(obj)/config.tmp: $(objtree)/.config FORCE
$(call if_changed,quote1)
quiet_cmd_quote1 = QUOTE $@
cmd_quote1 = sed -e 's/"/\\"/g' -e 's/^/"/' -e 's/$$/\\n"/' \
$< > $@
$(obj)/config.c: $(src)/config.c.in $(obj)/config.tmp FORCE
$(call if_changed,quote2)
quiet_cmd_quote2 = QUOTE $@
cmd_quote2 = sed -e '/CONFIG/{' \
-e 's/"CONFIG"\;/""/' \
-e 'r $(obj)/config.tmp' \
-e 'a""\;' \
-e '}' \
$< > $@

36
arch/um/kernel/checksum.c 普通文件
查看文件

@@ -0,0 +1,36 @@
#include "asm/uaccess.h"
#include "linux/errno.h"
#include "linux/module.h"
unsigned int arch_csum_partial(const unsigned char *buff, int len, int sum);
unsigned int csum_partial(unsigned char *buff, int len, int sum)
{
return arch_csum_partial(buff, len, sum);
}
EXPORT_SYMBOL(csum_partial);
unsigned int csum_partial_copy_to(const unsigned char *src,
unsigned char __user *dst, int len, int sum,
int *err_ptr)
{
if(copy_to_user(dst, src, len)){
*err_ptr = -EFAULT;
return(-1);
}
return(arch_csum_partial(src, len, sum));
}
unsigned int csum_partial_copy_from(const unsigned char __user *src,
unsigned char *dst, int len, int sum,
int *err_ptr)
{
if(copy_from_user(dst, src, len)){
*err_ptr = -EFAULT;
return(-1);
}
return arch_csum_partial(dst, len, sum);
}

32
arch/um/kernel/config.c.in 普通文件
查看文件

@@ -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:
*/

176
arch/um/kernel/dyn.lds.S 普通文件
查看文件

@@ -0,0 +1,176 @@
#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 */
_stext = .;
__init_begin = .;
.init.text : {
_sinittext = .;
*(.init.text)
_einittext = .;
}
. = ALIGN(4096);
/* Read-only sections, merged into text segment: */
.hash : { *(.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)
} =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 debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
}

91
arch/um/kernel/exec_kern.c 普通文件
查看文件

@@ -0,0 +1,91 @@
/*
* 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 "2_5compat.h"
#include "os.h"
#include "time_user.h"
#include "choose-mode.h"
#include "mode_kern.h"
void flush_thread(void)
{
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);
}
extern void log_exec(char **argv, void *tty);
static long execve1(char *file, char __user * __user *argv,
char *__user __user *env)
{
long error;
#ifdef CONFIG_TTY_LOG
log_exec(argv, current->tty);
#endif
error = do_execve(file, argv, env, &current->thread.regs);
if (error == 0){
task_lock(current);
current->ptrace &= ~PT_DTRACE;
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 *file, char __user *__user *argv,
char __user *__user *env)
{
long error;
char *filename;
lock_kernel();
filename = getname((char __user *) file);
error = PTR_ERR(filename);
if (IS_ERR(filename)) goto out;
error = execve1(filename, argv, env);
putname(filename);
out:
unlock_kernel();
return(error);
}
/*
* 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/exitcode.c 普通文件
查看文件

@@ -0,0 +1,73 @@
/*
* 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;
len = sprintf(page, "%d\n", uml_exitcode);
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("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:
*/

34
arch/um/kernel/gmon_syms.c 普通文件
查看文件

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/module.h"
extern void __bb_init_func(void *);
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);
/*
* 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:
*/

20
arch/um/kernel/gprof_syms.c 普通文件
查看文件

@@ -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:
*/

173
arch/um/kernel/helper.c 普通文件
查看文件

@@ -0,0 +1,173 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sched.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include "user.h"
#include "kern_util.h"
#include "user_util.h"
#include "os.h"
struct helper_data {
void (*pre_exec)(void*);
void *pre_data;
char **argv;
int fd;
};
/* Debugging aid, changed only from gdb */
int helper_pause = 0;
static void helper_hup(int sig)
{
}
static int helper_child(void *arg)
{
struct helper_data *data = arg;
char **argv = data->argv;
int errval;
if(helper_pause){
signal(SIGHUP, helper_hup);
pause();
}
if(data->pre_exec != NULL)
(*data->pre_exec)(data->pre_data);
execvp(argv[0], argv);
errval = errno;
printk("execvp of '%s' failed - errno = %d\n", argv[0], errno);
os_write_file(data->fd, &errval, sizeof(errval));
os_kill_process(os_getpid(), 0);
return(0);
}
/* Returns either the pid of the child process we run or -E* on failure.
* XXX The alloc_stack here breaks if this is called in the tracing thread */
int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv,
unsigned long *stack_out)
{
struct helper_data data;
unsigned long stack, sp;
int pid, fds[2], ret, n;
if((stack_out != NULL) && (*stack_out != 0))
stack = *stack_out;
else stack = alloc_stack(0, um_in_interrupt());
if(stack == 0)
return(-ENOMEM);
ret = os_pipe(fds, 1, 0);
if(ret < 0){
printk("run_helper : pipe failed, ret = %d\n", -ret);
goto out_free;
}
ret = os_set_exec_close(fds[1], 1);
if(ret < 0){
printk("run_helper : setting FD_CLOEXEC failed, ret = %d\n",
-ret);
goto out_close;
}
sp = stack + page_size() - sizeof(void *);
data.pre_exec = pre_exec;
data.pre_data = pre_data;
data.argv = argv;
data.fd = fds[1];
pid = clone(helper_child, (void *) sp, CLONE_VM | SIGCHLD, &data);
if(pid < 0){
printk("run_helper : clone failed, errno = %d\n", errno);
ret = -errno;
goto out_close;
}
os_close_file(fds[1]);
fds[1] = -1;
/*Read the errno value from the child.*/
n = os_read_file(fds[0], &ret, sizeof(ret));
if(n < 0){
printk("run_helper : read on pipe failed, ret = %d\n", -n);
ret = n;
os_kill_process(pid, 1);
}
else if(n != 0){
CATCH_EINTR(n = waitpid(pid, NULL, 0));
ret = -errno;
} else {
ret = pid;
}
out_close:
if (fds[1] != -1)
os_close_file(fds[1]);
os_close_file(fds[0]);
out_free:
if(stack_out == NULL)
free_stack(stack, 0);
else *stack_out = stack;
return(ret);
}
int run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags,
unsigned long *stack_out, int stack_order)
{
unsigned long stack, sp;
int pid, status;
stack = alloc_stack(stack_order, um_in_interrupt());
if(stack == 0) return(-ENOMEM);
sp = stack + (page_size() << stack_order) - sizeof(void *);
pid = clone(proc, (void *) sp, flags | SIGCHLD, arg);
if(pid < 0){
printk("run_helper_thread : clone failed, errno = %d\n",
errno);
return(-errno);
}
if(stack_out == NULL){
CATCH_EINTR(pid = waitpid(pid, &status, 0));
if(pid < 0){
printk("run_helper_thread - wait failed, errno = %d\n",
errno);
pid = -errno;
}
if(!WIFEXITED(status) || (WEXITSTATUS(status) != 0))
printk("run_helper_thread - thread returned status "
"0x%x\n", status);
free_stack(stack, stack_order);
}
else *stack_out = stack;
return(pid);
}
int helper_wait(int pid, int block)
{
int ret;
CATCH_EINTR(ret = waitpid(pid, NULL, WNOHANG));
if(ret < 0){
printk("helper_wait : waitpid failed, errno = %d\n", errno);
return(-errno);
}
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:
*/

61
arch/um/kernel/init_task.c 普通文件
查看文件

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/config.h"
#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"
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)
{
protect_memory(stack, (1 << CONFIG_KERNEL_STACK_ORDER) * PAGE_SIZE,
1, 1, 0, 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:
*/

查看文件

@@ -0,0 +1,59 @@
/*
* 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"
);
/*
* 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:
*/

查看文件

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include "user_util.h"
#include "kern_util.h"
#include "user.h"
#include "initrd.h"
#include "os.h"
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:
*/

178
arch/um/kernel/irq.c 普通文件
查看文件

@@ -0,0 +1,178 @@
/*
* 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/config.h"
#include "linux/kernel.h"
#include "linux/module.h"
#include "linux/smp.h"
#include "linux/irq.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"
/*
* 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].handler->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;
}
/*
* 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)
{
irq_enter();
__do_IRQ(irq, (struct pt_regs *) regs);
irq_exit();
return 1;
}
int um_request_irq(unsigned int irq, int fd, int type,
irqreturn_t (*handler)(int, void *, struct pt_regs *),
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);
static DEFINE_SPINLOCK(irq_spinlock);
unsigned long irq_lock(void)
{
unsigned long flags;
spin_lock_irqsave(&irq_spinlock, flags);
return(flags);
}
void irq_unlock(unsigned long flags)
{
spin_unlock_irqrestore(&irq_spinlock, flags);
}
/* presently hw_interrupt_type must define (startup || enable) &&
* disable && end */
static void dummy(unsigned int irq)
{
}
static struct hw_interrupt_type SIGIO_irq_type = {
.typename = "SIGIO",
.disable = dummy,
.enable = dummy,
.ack = dummy,
.end = dummy
};
static struct hw_interrupt_type SIGVTALRM_irq_type = {
.typename = "SIGVTALRM",
.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].handler = &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].handler = &SIGIO_irq_type;
enable_irq(i);
}
init_irq_signals(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:
*/

443
arch/um/kernel/irq_user.c 普通文件
查看文件

@@ -0,0 +1,443 @@
/*
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <sys/poll.h>
#include <sys/types.h>
#include <sys/time.h>
#include "user_util.h"
#include "kern_util.h"
#include "user.h"
#include "process.h"
#include "signal_user.h"
#include "sigio.h"
#include "irq_user.h"
#include "os.h"
struct irq_fd {
struct irq_fd *next;
void *id;
int fd;
int type;
int irq;
int pid;
int events;
int current_events;
int freed;
};
static struct irq_fd *active_fds = NULL;
static struct irq_fd **last_irq_ptr = &active_fds;
static struct pollfd *pollfds = NULL;
static int pollfds_num = 0;
static int pollfds_size = 0;
extern int io_count, intr_count;
void sigio_handler(int sig, union uml_pt_regs *regs)
{
struct irq_fd *irq_fd, *next;
int i, n;
if(smp_sigio_handler()) return;
while(1){
n = poll(pollfds, pollfds_num, 0);
if(n < 0){
if(errno == EINTR) continue;
printk("sigio_handler : poll returned %d, "
"errno = %d\n", n, errno);
break;
}
if(n == 0) break;
irq_fd = active_fds;
for(i = 0; i < pollfds_num; i++){
if(pollfds[i].revents != 0){
irq_fd->current_events = pollfds[i].revents;
pollfds[i].fd = -1;
}
irq_fd = irq_fd->next;
}
for(irq_fd = active_fds; irq_fd != NULL; irq_fd = next){
next = irq_fd->next;
if(irq_fd->current_events != 0){
irq_fd->current_events = 0;
do_IRQ(irq_fd->irq, regs);
/* This is here because the next irq may be
* freed in the handler. If a console goes
* away, both the read and write irqs will be
* freed. After do_IRQ, ->next will point to
* a good IRQ.
* Irqs can't be freed inside their handlers,
* so the next best thing is to have them
* marked as needing freeing, so that they
* can be freed here.
*/
next = irq_fd->next;
if(irq_fd->freed){
free_irq(irq_fd->irq, irq_fd->id);
free_irq_by_irq_and_dev(irq_fd->irq,
irq_fd->id);
}
}
}
}
}
int activate_ipi(int fd, int pid)
{
return(os_set_fd_async(fd, pid));
}
static void maybe_sigio_broken(int fd, int type)
{
if(isatty(fd)){
if((type == IRQ_WRITE) && !pty_output_sigio){
write_sigio_workaround();
add_sigio_fd(fd, 0);
}
else if((type == IRQ_READ) && !pty_close_sigio){
write_sigio_workaround();
add_sigio_fd(fd, 1);
}
}
}
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, size;
pid = os_getpid();
err = os_set_fd_async(fd, pid);
if(err < 0)
goto out;
new_fd = um_kmalloc(sizeof(*new_fd));
err = -ENOMEM;
if(new_fd == NULL)
goto out;
if(type == IRQ_READ) events = POLLIN | POLLPRI;
else events = POLLOUT;
*new_fd = ((struct irq_fd) { .next = NULL,
.id = dev_id,
.fd = fd,
.type = type,
.irq = irq,
.pid = pid,
.events = events,
.current_events = 0,
.freed = 0 } );
/* Critical section - locked by a spinlock because this stuff can
* be changed from interrupt handlers. The stuff above is done
* outside the lock because it allocates memory.
*/
/* Actually, it only looks like it can be called from interrupt
* context. The culprit is reactivate_fd, which calls
* maybe_sigio_broken, which calls write_sigio_workaround,
* which calls activate_fd. However, write_sigio_workaround should
* only be called once, at boot time. That would make it clear that
* this is called only from process context, and can be locked with
* a semaphore.
*/
flags = irq_lock();
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%x, 0x%x\n", irq_fd->id, dev_id);
goto out_unlock;
}
}
n = pollfds_num;
if(n == pollfds_size){
while(1){
/* 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, we free
* the buffer and try again.
*/
irq_unlock(flags);
size = (pollfds_num + 1) * sizeof(pollfds[0]);
tmp_pfd = um_kmalloc(size);
flags = irq_lock();
if(tmp_pfd == NULL)
goto out_unlock;
if(n == pollfds_size)
break;
kfree(tmp_pfd);
}
if(pollfds != NULL){
memcpy(tmp_pfd, pollfds,
sizeof(pollfds[0]) * pollfds_size);
kfree(pollfds);
}
pollfds = tmp_pfd;
pollfds_size++;
}
if(type == IRQ_WRITE)
fd = -1;
pollfds[pollfds_num] = ((struct pollfd) { .fd = fd,
.events = events,
.revents = 0 });
pollfds_num++;
*last_irq_ptr = new_fd;
last_irq_ptr = &new_fd->next;
irq_unlock(flags);
/* This calls activate_fd, so it has to be outside the critical
* section.
*/
maybe_sigio_broken(fd, type);
return(0);
out_unlock:
irq_unlock(flags);
kfree(new_fd);
out:
return(err);
}
static void free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg)
{
struct irq_fd **prev;
unsigned long flags;
int i = 0;
flags = irq_lock();
prev = &active_fds;
while(*prev != NULL){
if((*test)(*prev, arg)){
struct irq_fd *old_fd = *prev;
if((pollfds[i].fd != -1) &&
(pollfds[i].fd != (*prev)->fd)){
printk("free_irq_by_cb - mismatch between "
"active_fds and pollfds, fd %d vs %d\n",
(*prev)->fd, pollfds[i].fd);
goto out;
}
memcpy(&pollfds[i], &pollfds[i + 1],
(pollfds_num - i - 1) * sizeof(pollfds[0]));
pollfds_num--;
if(last_irq_ptr == &old_fd->next)
last_irq_ptr = prev;
*prev = (*prev)->next;
if(old_fd->type == IRQ_WRITE)
ignore_sigio_fd(old_fd->fd);
kfree(old_fd);
continue;
}
prev = &(*prev)->next;
i++;
}
out:
irq_unlock(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;
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;
}
if((pollfds[i].fd != -1) && (pollfds[i].fd != fd)){
printk("find_irq_by_fd - mismatch between active_fds and "
"pollfds, fd %d vs %d, need %d\n", irq->fd,
pollfds[i].fd, fd);
irq = NULL;
goto out;
}
*index_out = i;
out:
return(irq);
}
void free_irq_later(int irq, void *dev_id)
{
struct irq_fd *irq_fd;
unsigned long flags;
flags = irq_lock();
for(irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next){
if((irq_fd->irq == irq) && (irq_fd->id == dev_id))
break;
}
if(irq_fd == NULL){
printk("free_irq_later found no irq, irq = %d, "
"dev_id = 0x%p\n", irq, dev_id);
goto out;
}
irq_fd->freed = 1;
out:
irq_unlock(flags);
}
void reactivate_fd(int fd, int irqnum)
{
struct irq_fd *irq;
unsigned long flags;
int i;
flags = irq_lock();
irq = find_irq_by_fd(fd, irqnum, &i);
if(irq == NULL){
irq_unlock(flags);
return;
}
pollfds[i].fd = irq->fd;
irq_unlock(flags);
/* This calls activate_fd, so it has to be outside the critical
* section.
*/
maybe_sigio_broken(fd, irq->type);
}
void deactivate_fd(int fd, int irqnum)
{
struct irq_fd *irq;
unsigned long flags;
int i;
flags = irq_lock();
irq = find_irq_by_fd(fd, irqnum, &i);
if(irq == NULL)
goto out;
pollfds[i].fd = -1;
out:
irq_unlock(flags);
}
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 */
set_handler(SIGIO, SIG_IGN, 0, -1);
return(0);
}
void forward_ipi(int fd, int pid)
{
int err;
err = os_set_owner(fd, pid);
if(err < 0)
printk("forward_ipi: set_owner failed, fd = %d, me = %d, "
"target = %d, err = %d\n", fd, os_getpid(), pid, -err);
}
void forward_interrupts(int pid)
{
struct irq_fd *irq;
unsigned long flags;
int err;
flags = irq_lock();
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;
}
irq_unlock(flags);
}
void init_irq_signals(int on_sigstack)
{
__sighandler_t h;
int flags;
flags = on_sigstack ? SA_ONSTACK : 0;
if(timer_irq_inited) h = (__sighandler_t) alarm_handler;
else h = boot_timer_handler;
set_handler(SIGVTALRM, h, flags | SA_RESTART,
SIGUSR1, SIGIO, SIGWINCH, SIGALRM, -1);
set_handler(SIGIO, (__sighandler_t) sig_handler, flags | SA_RESTART,
SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
signal(SIGWINCH, SIG_IGN);
}
/*
* 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:
*/

137
arch/um/kernel/ksyms.c 普通文件
查看文件

@@ -0,0 +1,137 @@
/*
* Copyright (C) 2001 - 2004 Jeff Dike (jdike@addtoit.com)
* Licensed under the GPL
*/
#include "linux/config.h"
#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/delay.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"
#include "helper.h"
EXPORT_SYMBOL(stop);
EXPORT_SYMBOL(uml_physmem);
EXPORT_SYMBOL(set_signals);
EXPORT_SYMBOL(get_signals);
EXPORT_SYMBOL(kernel_thread);
EXPORT_SYMBOL(__const_udelay);
EXPORT_SYMBOL(__udelay);
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(page_to_phys);
EXPORT_SYMBOL(phys_to_page);
EXPORT_SYMBOL(high_physmem);
EXPORT_SYMBOL(empty_zero_page);
EXPORT_SYMBOL(um_virt_to_phys);
EXPORT_SYMBOL(__virt_to_page);
EXPORT_SYMBOL(to_phys);
EXPORT_SYMBOL(to_virt);
EXPORT_SYMBOL(mode_tt);
EXPORT_SYMBOL(handle_page_fault);
EXPORT_SYMBOL(find_iomem);
EXPORT_SYMBOL(end_iomem);
#ifdef CONFIG_MODE_TT
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(strncpy_from_user_skas);
EXPORT_SYMBOL(copy_to_user_skas);
EXPORT_SYMBOL(copy_from_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);
/* This is here because UML expands open to sys_open, not to a system
* call instruction.
*/
EXPORT_SYMBOL(sys_open);
EXPORT_SYMBOL(sys_lseek);
EXPORT_SYMBOL(sys_read);
EXPORT_SYMBOL(sys_wait4);
#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
#ifdef CONFIG_HIGHMEM
EXPORT_SYMBOL(kmap);
EXPORT_SYMBOL(kunmap);
EXPORT_SYMBOL(kmap_atomic);
EXPORT_SYMBOL(kunmap_atomic);
EXPORT_SYMBOL(kmap_atomic_to_page);
#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:
*/

271
arch/um/kernel/main.c 普通文件
查看文件

@@ -0,0 +1,271 @@
/*
* Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <sys/resource.h>
#include <sys/mman.h>
#include <sys/user.h>
#include <asm/page.h>
#include "user_util.h"
#include "kern_util.h"
#include "mem_user.h"
#include "signal_user.h"
#include "time_user.h"
#include "irq_user.h"
#include "user.h"
#include "init.h"
#include "mode.h"
#include "choose-mode.h"
#include "uml-config.h"
#include "irq_user.h"
#include "time_user.h"
#include "os.h"
/* Set in set_stklim, which is called from main and __wrap_malloc.
* __wrap_malloc only calls it if main hasn't started.
*/
unsigned long stacksizelim;
/* Set in main */
char *linux_prog;
#define PGD_BOUND (4 * 1024 * 1024)
#define STACKSIZE (8 * 1024 * 1024)
#define THREAD_NAME_LEN (256)
static void set_stklim(void)
{
struct rlimit lim;
if(getrlimit(RLIMIT_STACK, &lim) < 0){
perror("getrlimit");
exit(1);
}
if((lim.rlim_cur == RLIM_INFINITY) || (lim.rlim_cur > STACKSIZE)){
lim.rlim_cur = STACKSIZE;
if(setrlimit(RLIMIT_STACK, &lim) < 0){
perror("setrlimit");
exit(1);
}
}
stacksizelim = (lim.rlim_cur + PGD_BOUND - 1) & ~(PGD_BOUND - 1);
}
static __init void do_uml_initcalls(void)
{
initcall_t *call;
call = &__uml_initcall_start;
while (call < &__uml_initcall_end){;
(*call)();
call++;
}
}
static void last_ditch_exit(int sig)
{
CHOOSE_MODE(kmalloc_ok = 0, (void) 0);
signal(SIGINT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGHUP, SIG_DFL);
uml_cleanup();
exit(1);
}
extern int uml_exitcode;
extern void scan_elf_aux( char **envp);
int main(int argc, char **argv, char **envp)
{
char **new_argv;
sigset_t mask;
int ret, i;
/* Enable all signals except SIGIO - in some environments, we can
* enter with some signals blocked
*/
sigemptyset(&mask);
sigaddset(&mask, SIGIO);
if(sigprocmask(SIG_SETMASK, &mask, NULL) < 0){
perror("sigprocmask");
exit(1);
}
#ifdef UML_CONFIG_MODE_TT
/* Allocate memory for thread command lines */
if(argc < 2 || strlen(argv[1]) < THREAD_NAME_LEN - 1){
char padding[THREAD_NAME_LEN] = {
[ 0 ... THREAD_NAME_LEN - 2] = ' ', '\0'
};
new_argv = malloc((argc + 2) * sizeof(char*));
if(!new_argv) {
perror("Allocating extended argv");
exit(1);
}
new_argv[0] = argv[0];
new_argv[1] = padding;
for(i = 2; i <= argc; i++)
new_argv[i] = argv[i - 1];
new_argv[argc + 1] = NULL;
execvp(new_argv[0], new_argv);
perror("execing with extended args");
exit(1);
}
#endif
linux_prog = argv[0];
set_stklim();
new_argv = malloc((argc + 1) * sizeof(char *));
if(new_argv == NULL){
perror("Mallocing argv");
exit(1);
}
for(i=0;i<argc;i++){
new_argv[i] = strdup(argv[i]);
if(new_argv[i] == NULL){
perror("Mallocing an arg");
exit(1);
}
}
new_argv[argc] = NULL;
set_handler(SIGINT, last_ditch_exit, SA_ONESHOT | SA_NODEFER, -1);
set_handler(SIGTERM, last_ditch_exit, SA_ONESHOT | SA_NODEFER, -1);
set_handler(SIGHUP, last_ditch_exit, SA_ONESHOT | SA_NODEFER, -1);
scan_elf_aux( envp);
do_uml_initcalls();
ret = linux_main(argc, argv);
/* Disable SIGPROF - I have no idea why libc doesn't do this or turn
* off the profiling time, but UML dies with a SIGPROF just before
* exiting when profiling is active.
*/
change_sig(SIGPROF, 0);
/* Reboot */
if(ret){
int err;
printf("\n");
/* stop timers and set SIG*ALRM to be ignored */
disable_timer();
/* disable SIGIO for the fds and set SIGIO to be ignored */
err = deactivate_all_fds();
if(err)
printf("deactivate_all_fds failed, errno = %d\n",
-err);
/* Let any pending signals fire now. This ensures
* that they won't be delivered after the exec, when
* they are definitely not expected.
*/
unblock_signals();
execvp(new_argv[0], new_argv);
perror("Failed to exec kernel");
ret = 1;
}
printf("\n");
return(uml_exitcode);
}
#define CAN_KMALLOC() \
(kmalloc_ok && CHOOSE_MODE((os_getpid() != tracing_pid), 1))
extern void *__real_malloc(int);
void *__wrap_malloc(int size)
{
void *ret;
if(!CAN_KMALLOC())
return(__real_malloc(size));
else if(size <= PAGE_SIZE) /* finding contiguos pages can be hard*/
ret = um_kmalloc(size);
else ret = um_vmalloc(size);
/* glibc people insist that if malloc fails, errno should be
* set by malloc as well. So we do.
*/
if(ret == NULL)
errno = ENOMEM;
return(ret);
}
void *__wrap_calloc(int n, int size)
{
void *ptr = __wrap_malloc(n * size);
if(ptr == NULL) return(NULL);
memset(ptr, 0, n * size);
return(ptr);
}
extern void __real_free(void *);
extern unsigned long high_physmem;
void __wrap_free(void *ptr)
{
unsigned long addr = (unsigned long) ptr;
/* We need to know how the allocation happened, so it can be correctly
* freed. This is done by seeing what region of memory the pointer is
* in -
* physical memory - kmalloc/kfree
* kernel virtual memory - vmalloc/vfree
* anywhere else - malloc/free
* If kmalloc is not yet possible, then either high_physmem and/or
* end_vm are still 0 (as at startup), in which case we call free, or
* we have set them, but anyway addr has not been allocated from those
* areas. So, in both cases __real_free is called.
*
* CAN_KMALLOC is checked because it would be bad to free a buffer
* with kmalloc/vmalloc after they have been turned off during
* shutdown.
* XXX: However, we sometimes shutdown CAN_KMALLOC temporarily, so
* there is a possibility for memory leaks.
*/
if((addr >= uml_physmem) && (addr < high_physmem)){
if(CAN_KMALLOC())
kfree(ptr);
}
else if((addr >= start_vm) && (addr < end_vm)){
if(CAN_KMALLOC())
vfree(ptr);
}
else __real_free(ptr);
}
/*
* 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:
*/

359
arch/um/kernel/mem.c 普通文件
查看文件

@@ -0,0 +1,359 @@
/*
* 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"
extern char __binary_start;
/* Changed during early boot */
unsigned long *empty_zero_page = NULL;
unsigned long *empty_bad_page = NULL;
pgd_t swapper_pg_dir[PTRS_PER_PGD];
unsigned 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);
set_bit(PG_highmem, &page->flags);
set_page_count(page, 1);
__free_page(page);
}
}
#endif
void mem_init(void)
{
unsigned long start;
max_low_pfn = (high_physmem - uml_physmem) >> PAGE_SHIFT;
/* 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;
/* Fill in any hole at the start of the binary */
start = (unsigned long) &__binary_start & PAGE_MASK;
if(uml_physmem != start){
map_memory(uml_physmem, __pa(uml_physmem), start - uml_physmem,
1, 1, 0);
}
/* this will put all low memory onto the freelists */
totalram_pages = free_all_bootmem();
totalhigh_pages = highmem >> PAGE_SHIFT;
totalram_pages += totalhigh_pages;
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
}
static void __init fixrange_init(unsigned long start, unsigned long end,
pgd_t *pgd_base)
{
pgd_t *pgd;
pmd_t *pmd;
pte_t *pte;
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++) {
pmd = (pmd_t *)pgd;
for (; (j < PTRS_PER_PMD) && (vaddr != end); pmd++, j++) {
if (pmd_none(*pmd)) {
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();
}
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)
{
#if 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<sizeof(zones_size)/sizeof(zones_size[0]);i++)
zones_size[i] = 0;
zones_size[0] = (end_iomem >> PAGE_SHIFT) - (uml_physmem >> PAGE_SHIFT);
zones_size[2] = highmem >> PAGE_SHIFT;
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, int 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),
&current->thread.fault_addr,
&current->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));
set_page_count(virt_to_page(start), 1);
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;
}
/*
* 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:
*/

273
arch/um/kernel/mem_user.c 普通文件
查看文件

@@ -0,0 +1,273 @@
/*
* arch/um/kernel/mem_user.c
*
* BRIEF MODULE DESCRIPTION
* user side memory routines for supporting IO memory inside user mode linux
*
* Copyright (C) 2001 RidgeRun, Inc.
* Author: RidgeRun, Inc.
* Greg Lonnon glonnon@ridgerun.com or info@ridgerun.com
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdarg.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/mman.h>
#include "kern_util.h"
#include "user.h"
#include "user_util.h"
#include "mem_user.h"
#include "init.h"
#include "os.h"
#include "tempfile.h"
#include "kern_constants.h"
#define TEMPNAME_TEMPLATE "vm_file-XXXXXX"
static int create_tmp_file(unsigned long len)
{
int fd, err;
char zero;
fd = make_tempfile(TEMPNAME_TEMPLATE, NULL, 1);
if(fd < 0) {
os_print_error(fd, "make_tempfile");
exit(1);
}
err = os_mode_fd(fd, 0777);
if(err < 0){
os_print_error(err, "os_mode_fd");
exit(1);
}
err = os_seek_file(fd, len);
if(err < 0){
os_print_error(err, "os_seek_file");
exit(1);
}
zero = 0;
err = os_write_file(fd, &zero, 1);
if(err != 1){
os_print_error(err, "os_write_file");
exit(1);
}
return(fd);
}
void check_tmpexec(void)
{
void *addr;
int err, fd = create_tmp_file(UM_KERN_PAGE_SIZE);
addr = mmap(NULL, UM_KERN_PAGE_SIZE,
PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0);
printf("Checking PROT_EXEC mmap in /tmp...");
fflush(stdout);
if(addr == MAP_FAILED){
err = errno;
perror("failed");
if(err == EPERM)
printf("/tmp must be not mounted noexec\n");
exit(1);
}
printf("OK\n");
munmap(addr, UM_KERN_PAGE_SIZE);
os_close_file(fd);
}
static int have_devanon = 0;
void check_devanon(void)
{
int fd;
printk("Checking for /dev/anon on the host...");
fd = open("/dev/anon", O_RDWR);
if(fd < 0){
printk("Not available (open failed with errno %d)\n", errno);
return;
}
printk("OK\n");
have_devanon = 1;
}
static int create_anon_file(unsigned long len)
{
void *addr;
int fd;
fd = open("/dev/anon", O_RDWR);
if(fd < 0) {
os_print_error(fd, "opening /dev/anon");
exit(1);
}
addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if(addr == MAP_FAILED){
perror("mapping physmem file");
exit(1);
}
munmap(addr, len);
return(fd);
}
int create_mem_file(unsigned long len)
{
int err, fd;
if(have_devanon)
fd = create_anon_file(len);
else fd = create_tmp_file(len);
err = os_set_exec_close(fd, 1);
if(err < 0)
os_print_error(err, "exec_close");
return(fd);
}
struct iomem_region *iomem_regions = NULL;
int iomem_size = 0;
static int __init parse_iomem(char *str, int *add)
{
struct iomem_region *new;
struct uml_stat buf;
char *file, *driver;
int fd, err, size;
driver = str;
file = strchr(str,',');
if(file == NULL){
printf("parse_iomem : failed to parse iomem\n");
goto out;
}
*file = '\0';
file++;
fd = os_open_file(file, of_rdwr(OPENFLAGS()), 0);
if(fd < 0){
os_print_error(fd, "parse_iomem - Couldn't open io file");
goto out;
}
err = os_stat_fd(fd, &buf);
if(err < 0){
os_print_error(err, "parse_iomem - cannot stat_fd file");
goto out_close;
}
new = malloc(sizeof(*new));
if(new == NULL){
perror("Couldn't allocate iomem_region struct");
goto out_close;
}
size = (buf.ust_size + UM_KERN_PAGE_SIZE) & ~(UM_KERN_PAGE_SIZE - 1);
*new = ((struct iomem_region) { .next = iomem_regions,
.driver = driver,
.fd = fd,
.size = size,
.phys = 0,
.virt = 0 });
iomem_regions = new;
iomem_size += new->size + UM_KERN_PAGE_SIZE;
return(0);
out_close:
os_close_file(fd);
out:
return(1);
}
__uml_setup("iomem=", parse_iomem,
"iomem=<name>,<file>\n"
" Configure <file> as an IO memory region named <name>.\n\n"
);
int protect_memory(unsigned long addr, unsigned long len, int r, int w, int x,
int must_succeed)
{
int err;
err = os_protect_memory((void *) addr, len, r, w, x);
if(err < 0){
if(must_succeed)
panic("protect failed, err = %d", -err);
else return(err);
}
return(0);
}
#if 0
/* Debugging facility for dumping stuff out to the host, avoiding the timing
* problems that come with printf and breakpoints.
* Enable in case of emergency.
*/
int logging = 1;
int logging_fd = -1;
int logging_line = 0;
char logging_buf[512];
void log(char *fmt, ...)
{
va_list ap;
struct timeval tv;
struct openflags flags;
if(logging == 0) return;
if(logging_fd < 0){
flags = of_create(of_trunc(of_rdwr(OPENFLAGS())));
logging_fd = os_open_file("log", flags, 0644);
}
gettimeofday(&tv, NULL);
sprintf(logging_buf, "%d\t %u.%u ", logging_line++, tv.tv_sec,
tv.tv_usec);
va_start(ap, fmt);
vsprintf(&logging_buf[strlen(logging_buf)], fmt, ap);
va_end(ap);
write(logging_fd, logging_buf, strlen(logging_buf));
}
#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:
*/

478
arch/um/kernel/physmem.c 普通文件
查看文件

@@ -0,0 +1,478 @@
/*
* 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 "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, (*n)->rb_parent, 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 physmem_size;
void *to_virt(unsigned long phys)
{
return((void *) uml_physmem + phys);
}
unsigned long to_phys(void *virt)
{
return(((unsigned long) virt) - uml_physmem);
}
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_pages + 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];
set_page_count(p, 0);
SetPageReserved(p);
INIT_LIST_HEAD(&p->lru);
}
max_mapnr = total_pages;
return(0);
}
struct page *phys_to_page(const unsigned long phys)
{
return(&mem_map[phys >> PAGE_SHIFT]);
}
struct page *__virt_to_page(const unsigned long virt)
{
return(&mem_map[__pa(virt) >> PAGE_SHIFT]);
}
phys_t page_to_phys(struct page *page)
{
return((page - mem_map) << PAGE_SHIFT);
}
pte_t mk_pte(struct page *page, pgprot_t pgprot)
{
pte_t pte;
pte_set_val(pte, page_to_phys(page), pgprot);
if(pte_present(pte))
pte_mknewprot(pte_mknewpage(pte));
return(pte);
}
/* 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);
}
}
#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
void setup_physmem(unsigned long start, unsigned long reserve_end,
unsigned long len, unsigned 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);
}
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"
);
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);
}
}
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);
/*
* 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:
*/

423
arch/um/kernel/process.c 普通文件
查看文件

@@ -0,0 +1,423 @@
/*
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sched.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <setjmp.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <asm/unistd.h>
#include <asm/page.h>
#include "user_util.h"
#include "kern_util.h"
#include "user.h"
#include "process.h"
#include "signal_kern.h"
#include "signal_user.h"
#include "sysdep/ptrace.h"
#include "sysdep/sigcontext.h"
#include "irq_user.h"
#include "ptrace_user.h"
#include "time_user.h"
#include "init.h"
#include "os.h"
#include "uml-config.h"
#include "ptrace_user.h"
#include "choose-mode.h"
#include "mode.h"
#ifdef UML_CONFIG_MODE_SKAS
#include "skas.h"
#include "skas_ptrace.h"
#include "registers.h"
#endif
void init_new_thread_stack(void *sig_stack, void (*usr1_handler)(int))
{
int flags = 0, pages;
if(sig_stack != NULL){
pages = (1 << UML_CONFIG_KERNEL_STACK_ORDER);
set_sigstack(sig_stack, pages * page_size());
flags = SA_ONSTACK;
}
if(usr1_handler) set_handler(SIGUSR1, usr1_handler, flags, -1);
}
void init_new_thread_signals(int altstack)
{
int flags = altstack ? SA_ONSTACK : 0;
set_handler(SIGSEGV, (__sighandler_t) sig_handler, flags,
SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
set_handler(SIGTRAP, (__sighandler_t) sig_handler, flags,
SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
set_handler(SIGFPE, (__sighandler_t) sig_handler, flags,
SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
set_handler(SIGILL, (__sighandler_t) sig_handler, flags,
SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
set_handler(SIGBUS, (__sighandler_t) sig_handler, flags,
SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
set_handler(SIGWINCH, (__sighandler_t) sig_handler, flags,
SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
set_handler(SIGUSR2, (__sighandler_t) sig_handler,
flags, SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
signal(SIGHUP, SIG_IGN);
init_irq_signals(altstack);
}
struct tramp {
int (*tramp)(void *);
void *tramp_data;
unsigned long temp_stack;
int flags;
int pid;
};
/* See above for why sigkill is here */
int sigkill = SIGKILL;
int outer_tramp(void *arg)
{
struct tramp *t;
int sig = sigkill;
t = arg;
t->pid = clone(t->tramp, (void *) t->temp_stack + page_size()/2,
t->flags, t->tramp_data);
if(t->pid > 0) wait_for_stop(t->pid, SIGSTOP, PTRACE_CONT, NULL);
kill(os_getpid(), sig);
_exit(0);
}
int start_fork_tramp(void *thread_arg, unsigned long temp_stack,
int clone_flags, int (*tramp)(void *))
{
struct tramp arg;
unsigned long sp;
int new_pid, status, err;
/* The trampoline will run on the temporary stack */
sp = stack_sp(temp_stack);
clone_flags |= CLONE_FILES | SIGCHLD;
arg.tramp = tramp;
arg.tramp_data = thread_arg;
arg.temp_stack = temp_stack;
arg.flags = clone_flags;
/* Start the process and wait for it to kill itself */
new_pid = clone(outer_tramp, (void *) sp, clone_flags, &arg);
if(new_pid < 0)
return(new_pid);
CATCH_EINTR(err = waitpid(new_pid, &status, 0));
if(err < 0)
panic("Waiting for outer trampoline failed - errno = %d",
errno);
if(!WIFSIGNALED(status) || (WTERMSIG(status) != SIGKILL))
panic("outer trampoline didn't exit with SIGKILL, "
"status = %d", status);
return(arg.pid);
}
static int ptrace_child(void *arg)
{
int ret;
int pid = os_getpid(), ppid = getppid();
int sc_result;
if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){
perror("ptrace");
os_kill_process(pid, 0);
}
os_stop_process(pid);
/*This syscall will be intercepted by the parent. Don't call more than
* once, please.*/
sc_result = os_getpid();
if (sc_result == pid)
ret = 1; /*Nothing modified by the parent, we are running
normally.*/
else if (sc_result == ppid)
ret = 0; /*Expected in check_ptrace and check_sysemu when they
succeed in modifying the stack frame*/
else
ret = 2; /*Serious trouble! This could be caused by a bug in
host 2.6 SKAS3/2.6 patch before release -V6, together
with a bug in the UML code itself.*/
_exit(ret);
}
static int start_ptraced_child(void **stack_out)
{
void *stack;
unsigned long sp;
int pid, n, status;
stack = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if(stack == MAP_FAILED)
panic("check_ptrace : mmap failed, errno = %d", errno);
sp = (unsigned long) stack + PAGE_SIZE - sizeof(void *);
pid = clone(ptrace_child, (void *) sp, SIGCHLD, NULL);
if(pid < 0)
panic("check_ptrace : clone failed, errno = %d", errno);
CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
if(n < 0)
panic("check_ptrace : wait failed, errno = %d", errno);
if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP))
panic("check_ptrace : expected SIGSTOP, got status = %d",
status);
*stack_out = stack;
return(pid);
}
/* When testing for SYSEMU support, if it is one of the broken versions, we must
* just avoid using sysemu, not panic, but only if SYSEMU features are broken.
* So only for SYSEMU features we test mustpanic, while normal host features
* must work anyway!*/
static int stop_ptraced_child(int pid, void *stack, int exitcode, int mustpanic)
{
int status, n, ret = 0;
if(ptrace(PTRACE_CONT, pid, 0, 0) < 0)
panic("check_ptrace : ptrace failed, errno = %d", errno);
CATCH_EINTR(n = waitpid(pid, &status, 0));
if(!WIFEXITED(status) || (WEXITSTATUS(status) != exitcode)) {
int exit_with = WEXITSTATUS(status);
if (exit_with == 2)
printk("check_ptrace : child exited with status 2. "
"Serious trouble happening! Try updating your "
"host skas patch!\nDisabling SYSEMU support.");
printk("check_ptrace : child exited with exitcode %d, while "
"expecting %d; status 0x%x", exit_with,
exitcode, status);
if (mustpanic)
panic("\n");
else
printk("\n");
ret = -1;
}
if(munmap(stack, PAGE_SIZE) < 0)
panic("check_ptrace : munmap failed, errno = %d", errno);
return ret;
}
static int force_sysemu_disabled = 0;
static int __init nosysemu_cmd_param(char *str, int* add)
{
force_sysemu_disabled = 1;
return 0;
}
__uml_setup("nosysemu", nosysemu_cmd_param,
"nosysemu\n"
" Turns off syscall emulation patch for ptrace (SYSEMU) on.\n"
" SYSEMU is a performance-patch introduced by Laurent Vivier. It changes\n"
" behaviour of ptrace() and helps reducing host context switch rate.\n"
" To make it working, you need a kernel patch for your host, too.\n"
" See http://perso.wanadoo.fr/laurent.vivier/UML/ for further information.\n\n");
static void __init check_sysemu(void)
{
void *stack;
int pid, syscall, n, status, count=0;
printk("Checking syscall emulation patch for ptrace...");
sysemu_supported = 0;
pid = start_ptraced_child(&stack);
if(ptrace(PTRACE_SYSEMU, pid, 0, 0) < 0)
goto fail;
CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
if (n < 0)
panic("check_sysemu : wait failed, errno = %d", errno);
if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP))
panic("check_sysemu : expected SIGTRAP, "
"got status = %d", status);
n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET,
os_getpid());
if(n < 0)
panic("check_sysemu : failed to modify system "
"call return, errno = %d", errno);
if (stop_ptraced_child(pid, stack, 0, 0) < 0)
goto fail_stopped;
sysemu_supported = 1;
printk("OK\n");
set_using_sysemu(!force_sysemu_disabled);
printk("Checking advanced syscall emulation patch for ptrace...");
pid = start_ptraced_child(&stack);
while(1){
count++;
if(ptrace(PTRACE_SYSEMU_SINGLESTEP, pid, 0, 0) < 0)
goto fail;
CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
if(n < 0)
panic("check_ptrace : wait failed, errno = %d", errno);
if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP))
panic("check_ptrace : expected (SIGTRAP|SYSCALL_TRAP), "
"got status = %d", status);
syscall = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_NR_OFFSET,
0);
if(syscall == __NR_getpid){
if (!count)
panic("check_ptrace : SYSEMU_SINGLESTEP doesn't singlestep");
n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET,
os_getpid());
if(n < 0)
panic("check_sysemu : failed to modify system "
"call return, errno = %d", errno);
break;
}
}
if (stop_ptraced_child(pid, stack, 0, 0) < 0)
goto fail_stopped;
sysemu_supported = 2;
printk("OK\n");
if ( !force_sysemu_disabled )
set_using_sysemu(sysemu_supported);
return;
fail:
stop_ptraced_child(pid, stack, 1, 0);
fail_stopped:
printk("missing\n");
}
void __init check_ptrace(void)
{
void *stack;
int pid, syscall, n, status;
printk("Checking that ptrace can change system call numbers...");
pid = start_ptraced_child(&stack);
if (ptrace(PTRACE_OLDSETOPTIONS, pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0)
panic("check_ptrace: PTRACE_SETOPTIONS failed, errno = %d", errno);
while(1){
if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
panic("check_ptrace : ptrace failed, errno = %d",
errno);
CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
if(n < 0)
panic("check_ptrace : wait failed, errno = %d", errno);
if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP + 0x80))
panic("check_ptrace : expected SIGTRAP + 0x80, "
"got status = %d", status);
syscall = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_NR_OFFSET,
0);
if(syscall == __NR_getpid){
n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET,
__NR_getppid);
if(n < 0)
panic("check_ptrace : failed to modify system "
"call, errno = %d", errno);
break;
}
}
stop_ptraced_child(pid, stack, 0, 1);
printk("OK\n");
check_sysemu();
}
int run_kernel_thread(int (*fn)(void *), void *arg, void **jmp_ptr)
{
sigjmp_buf buf;
int n;
*jmp_ptr = &buf;
n = sigsetjmp(buf, 1);
if(n != 0)
return(n);
(*fn)(arg);
return(0);
}
void forward_pending_sigio(int target)
{
sigset_t sigs;
if(sigpending(&sigs))
panic("forward_pending_sigio : sigpending failed");
if(sigismember(&sigs, SIGIO))
kill(target, SIGIO);
}
#ifdef UML_CONFIG_MODE_SKAS
static inline int check_skas3_ptrace_support(void)
{
struct ptrace_faultinfo fi;
void *stack;
int pid, n, ret = 1;
printf("Checking for the skas3 patch in the host...");
pid = start_ptraced_child(&stack);
n = ptrace(PTRACE_FAULTINFO, pid, 0, &fi);
if (n < 0) {
if(errno == EIO)
printf("not found\n");
else {
perror("not found");
}
ret = 0;
} else {
printf("found\n");
}
init_registers(pid);
stop_ptraced_child(pid, stack, 1, 1);
return(ret);
}
int can_do_skas(void)
{
int ret = 1;
printf("Checking for /proc/mm...");
if (os_access("/proc/mm", OS_ACC_W_OK) < 0) {
printf("not found\n");
ret = 0;
goto out;
} else {
printf("found\n");
}
ret = check_skas3_ptrace_support();
out:
return ret;
}
#else
int can_do_skas(void)
{
return(0);
}
#endif

查看文件

@@ -0,0 +1,500 @@
/*
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
* Copyright 2003 PathScale, Inc.
* Licensed under the GPL
*/
#include "linux/config.h"
#include "linux/kernel.h"
#include "linux/sched.h"
#include "linux/interrupt.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 "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 "signal_user.h"
#include "init.h"
#include "irq_user.h"
#include "mem_user.h"
#include "time_user.h"
#include "tlb.h"
#include "frame_kern.h"
#include "sigcontext.h"
#include "2_5compat.h"
#include "os.h"
#include "mode.h"
#include "mode_kern.h"
#include "choose-mode.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 } };
struct task_struct *get_task(int pid, int require)
{
struct task_struct *ret;
read_lock(&tasklist_lock);
ret = find_task_by_pid(pid);
read_unlock(&tasklist_lock);
if(require && (ret == NULL)) panic("get_task couldn't find a task\n");
return(ret);
}
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;
int 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, NULL, 0, NULL,
NULL);
if(pid < 0)
panic("do_fork failed in kernel_thread, errno = %d", pid);
return(pid);
}
void switch_mm(struct mm_struct *prev, struct mm_struct *next,
struct task_struct *tsk)
{
int cpu = smp_processor_id();
if (prev != next)
cpu_clear(cpu, prev->cpu_vm_mask);
cpu_set(cpu, next->cpu_vm_mask);
}
void set_current(void *t)
{
struct task_struct *task = t;
cpu_tasks[task->thread_info->cpu] = ((struct cpu_task)
{ external_pid(task), task });
}
void *_switch_to(void *prev, void *next, void *last)
{
return(CHOOSE_MODE(switch_to_tt(prev, next),
switch_to_skas(prev, next)));
}
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)
{
CHOOSE_MODE(exit_thread_tt(), exit_thread_skas());
unprotect_stack((unsigned long) current_thread);
}
void *get_current(void)
{
return(current);
}
void prepare_to_copy(struct task_struct *tsk)
{
}
int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
unsigned long stack_top, struct task_struct * p,
struct pt_regs *regs)
{
p->thread = (struct thread_struct) INIT_THREAD;
return(CHOOSE_MODE_PROC(copy_thread_tt, copy_thread_skas, nr,
clone_flags, sp, stack_top, p, regs));
}
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)
{
uml_idle_timer();
atomic_inc(&init_mm.mm_count);
current->mm = &init_mm;
current->active_mm = &init_mm;
while(1){
/* endless idle loop with no priority at all */
SET_PRI(current);
/*
* 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);
}
unsigned long page_mask(void)
{
return(PAGE_MASK);
}
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;
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);
if(!pte_present(*pte))
return(ERR_PTR(-EINVAL));
if(pte_out != NULL)
*pte_out = *pte;
return((void *) (pte_val(*pte) & 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(&current->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));
}
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 void remove_umid_dir(void);
__uml_exitcall(remove_umid_dir);
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)
{
char *new;
new = kmalloc(strlen(string) + 1, GFP_KERNEL);
if(new == NULL) return(NULL);
strcpy(new, string);
return(new);
}
void *get_init_task(void)
{
return(&init_thread_union.thread_info.task);
}
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 um_in_interrupt(void)
{
return(in_interrupt());
}
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 *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("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;
}
unsigned long arch_align_stack(unsigned long sp)
{
if (randomize_va_space)
sp -= get_random_int() % 8192;
return sp & ~0xf;
}
/*
* 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:
*/

388
arch/um/kernel/ptrace.c 普通文件
查看文件

@@ -0,0 +1,388 @@
/*
* 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"
/*
* Called by kernel/ptrace.c when detaching..
*/
void ptrace_disable(struct task_struct *child)
{
child->ptrace &= ~PT_DTRACE;
child->thread.singlestep_syscall = 0;
}
long sys_ptrace(long request, long pid, long addr, long data)
{
struct task_struct *child;
int i, ret;
lock_kernel();
ret = -EPERM;
if (request == PTRACE_TRACEME) {
/* are we already being traced? */
if (current->ptrace & PT_PTRACED)
goto out;
ret = security_ptrace(current->parent, current);
if (ret)
goto out;
/* set the ptrace bit in the process flags. */
current->ptrace |= PT_PTRACED;
ret = 0;
goto out;
}
ret = -ESRCH;
read_lock(&tasklist_lock);
child = find_task_by_pid(pid);
if (child)
get_task_struct(child);
read_unlock(&tasklist_lock);
if (!child)
goto out;
ret = -EPERM;
if (pid == 1) /* you may not mess with init */
goto out_tsk;
if (request == PTRACE_ATTACH) {
ret = ptrace_attach(child);
goto out_tsk;
}
ret = ptrace_check_attach(child, request == PTRACE_KILL);
if (ret < 0)
goto out_tsk;
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, (unsigned long __user *) data);
break;
}
/* read the word at location addr in the USER area. */
case PTRACE_PEEKUSR: {
unsigned long tmp;
ret = -EIO;
if ((addr & 3) || addr < 0)
break;
tmp = 0; /* Default return condition */
if(addr < MAX_REG_OFFSET){
tmp = getreg(child, addr);
}
else if((addr >= offsetof(struct user, u_debugreg[0])) &&
(addr <= offsetof(struct user, u_debugreg[7]))){
addr -= offsetof(struct user, u_debugreg[0]);
addr = addr >> 2;
tmp = child->thread.arch.debugregs[addr];
}
ret = put_user(tmp, (unsigned long __user *) 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 = -EIO;
if ((addr & 3) || addr < 0)
break;
if (addr < MAX_REG_OFFSET) {
ret = putreg(child, addr, data);
break;
}
#if 0 /* XXX x86_64 */
else if((addr >= offsetof(struct user, u_debugreg[0])) &&
(addr <= offsetof(struct user, u_debugreg[7]))){
addr -= offsetof(struct user, u_debugreg[0]);
addr = addr >> 2;
if((addr == 4) || (addr == 5)) break;
child->thread.arch.debugregs[addr] = data;
ret = 0;
}
#endif
break;
case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
case PTRACE_CONT: { /* restart after signal. */
ret = -EIO;
if ((unsigned long) data > _NSIG)
break;
child->ptrace &= ~PT_DTRACE;
child->thread.singlestep_syscall = 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;
child->ptrace &= ~PT_DTRACE;
child->thread.singlestep_syscall = 0;
child->exit_code = SIGKILL;
wake_up_process(child);
break;
}
case PTRACE_SINGLESTEP: { /* set the trap flag. */
ret = -EIO;
if ((unsigned long) data > _NSIG)
break;
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
child->ptrace |= PT_DTRACE;
child->thread.singlestep_syscall = 0;
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, (unsigned long *)data,
MAX_REG_OFFSET)) {
ret = -EIO;
break;
}
for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) {
__put_user(getreg(child, i),
(unsigned long __user *) data);
data += sizeof(long);
}
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, (unsigned *)data,
MAX_REG_OFFSET)) {
ret = -EIO;
break;
}
for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) {
__get_user(tmp, (unsigned long __user *) data);
putreg(child, i, tmp);
data += sizeof(long);
}
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_FAULTINFO: {
struct ptrace_faultinfo fault;
fault = ((struct ptrace_faultinfo)
{ .is_write = child->thread.err,
.addr = child->thread.cr2 });
ret = copy_to_user((unsigned long __user *) data, &fault,
sizeof(fault));
if(ret)
break;
break;
}
case PTRACE_SIGPENDING:
ret = copy_to_user((unsigned long __user *) data,
&child->pending.signal,
sizeof(child->pending.signal));
break;
case PTRACE_LDT: {
struct ptrace_ldt ldt;
if(copy_from_user(&ldt, (unsigned long __user *) data,
sizeof(ldt))){
ret = -EIO;
break;
}
/* This one is confusing, so just punt and return -EIO for
* now
*/
ret = -EIO;
break;
}
#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
default:
ret = ptrace_request(child, request, addr, data);
break;
}
out_tsk:
put_task_struct(child);
out:
unlock_kernel();
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(current,
UPT_SYSCALL_NR(&regs->regs),
UPT_SYSCALL_ARG1(&regs->regs),
UPT_SYSCALL_ARG2(&regs->regs),
UPT_SYSCALL_ARG3(&regs->regs),
UPT_SYSCALL_ARG4(&regs->regs));
else
audit_syscall_exit(current,
UPT_SYSCALL_RET(&regs->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;
}
}
/*
* 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:
*/

79
arch/um/kernel/reboot.c 普通文件
查看文件

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2000, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/module.h"
#include "linux/sched.h"
#include "user_util.h"
#include "kern_util.h"
#include "kern.h"
#include "os.h"
#include "mode.h"
#include "choose-mode.h"
#ifdef CONFIG_SMP
static void kill_idlers(int me)
{
#ifdef CONFIG_MODE_TT
struct task_struct *p;
int i;
for(i = 0; i < sizeof(idle_threads)/sizeof(idle_threads[0]); 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)
{
kill_off_processes();
do_uml_exitcalls();
}
void machine_restart(char * __unused)
{
do_uml_exitcalls();
kill_off_processes();
CHOOSE_MODE(reboot_tt(), reboot_skas());
}
EXPORT_SYMBOL(machine_restart);
void machine_power_off(void)
{
do_uml_exitcalls();
kill_off_processes();
CHOOSE_MODE(halt_tt(), halt_skas());
}
EXPORT_SYMBOL(machine_power_off);
void machine_halt(void)
{
machine_power_off();
}
EXPORT_SYMBOL(machine_halt);
/*
* 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/resource.c 普通文件
查看文件

@@ -0,0 +1,23 @@
/*
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/pci.h"
unsigned long resource_fixup(struct pci_dev * dev, struct resource * res,
unsigned long start, unsigned long size)
{
return start;
}
/*
* 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:
*/

63
arch/um/kernel/sigio_kern.c 普通文件
查看文件

@@ -0,0 +1,63 @@
/*
* 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"
/* Protected by sigio_lock() called from write_sigio_workaround */
static int sigio_irq_fd = -1;
static irqreturn_t sigio_interrupt(int irq, void *data, struct pt_regs *unused)
{
read_sigio_fd(sigio_irq_fd);
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,
SA_INTERRUPT | SA_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);
}
static DEFINE_SPINLOCK(sigio_spinlock);
void sigio_lock(void)
{
spin_lock(&sigio_spinlock);
}
void sigio_unlock(void)
{
spin_unlock(&sigio_spinlock);
}
/*
* 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:
*/

431
arch/um/kernel/sigio_user.c 普通文件
查看文件

@@ -0,0 +1,431 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <unistd.h>
#include <stdlib.h>
#include <termios.h>
#include <pty.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <sched.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include "init.h"
#include "user.h"
#include "kern_util.h"
#include "user_util.h"
#include "sigio.h"
#include "helper.h"
#include "os.h"
/* Changed during early boot */
int pty_output_sigio = 0;
int pty_close_sigio = 0;
/* Used as a flag during SIGIO testing early in boot */
static volatile int got_sigio = 0;
void __init handler(int sig)
{
got_sigio = 1;
}
struct openpty_arg {
int master;
int slave;
int err;
};
static void openpty_cb(void *arg)
{
struct openpty_arg *info = arg;
info->err = 0;
if(openpty(&info->master, &info->slave, NULL, NULL, NULL))
info->err = -errno;
}
void __init check_one_sigio(void (*proc)(int, int))
{
struct sigaction old, new;
struct openpty_arg pty = { .master = -1, .slave = -1 };
int master, slave, err;
initial_thread_cb(openpty_cb, &pty);
if(pty.err){
printk("openpty failed, errno = %d\n", -pty.err);
return;
}
master = pty.master;
slave = pty.slave;
if((master == -1) || (slave == -1)){
printk("openpty failed to allocate a pty\n");
return;
}
/* Not now, but complain so we now where we failed. */
err = raw(master);
if (err < 0)
panic("check_sigio : __raw failed, errno = %d\n", -err);
err = os_sigio_async(master, slave);
if(err < 0)
panic("tty_fds : sigio_async failed, err = %d\n", -err);
if(sigaction(SIGIO, NULL, &old) < 0)
panic("check_sigio : sigaction 1 failed, errno = %d\n", errno);
new = old;
new.sa_handler = handler;
if(sigaction(SIGIO, &new, NULL) < 0)
panic("check_sigio : sigaction 2 failed, errno = %d\n", errno);
got_sigio = 0;
(*proc)(master, slave);
os_close_file(master);
os_close_file(slave);
if(sigaction(SIGIO, &old, NULL) < 0)
panic("check_sigio : sigaction 3 failed, errno = %d\n", errno);
}
static void tty_output(int master, int slave)
{
int n;
char buf[512];
printk("Checking that host ptys support output SIGIO...");
memset(buf, 0, sizeof(buf));
while(os_write_file(master, buf, sizeof(buf)) > 0) ;
if(errno != EAGAIN)
panic("check_sigio : write failed, errno = %d\n", errno);
while(((n = os_read_file(slave, buf, sizeof(buf))) > 0) && !got_sigio) ;
if (got_sigio) {
printk("Yes\n");
pty_output_sigio = 1;
} else if (n == -EAGAIN) {
printk("No, enabling workaround\n");
} else {
panic("check_sigio : read failed, err = %d\n", n);
}
}
static void tty_close(int master, int slave)
{
printk("Checking that host ptys support SIGIO on close...");
os_close_file(slave);
if(got_sigio){
printk("Yes\n");
pty_close_sigio = 1;
}
else printk("No, enabling workaround\n");
}
void __init check_sigio(void)
{
if((os_access("/dev/ptmx", OS_ACC_R_OK) < 0) &&
(os_access("/dev/ptyp0", OS_ACC_R_OK) < 0)){
printk("No pseudo-terminals available - skipping pty SIGIO "
"check\n");
return;
}
check_one_sigio(tty_output);
check_one_sigio(tty_close);
}
/* Protected by sigio_lock(), also used by sigio_cleanup, which is an
* exitcall.
*/
static int write_sigio_pid = -1;
/* These arrays are initialized before the sigio thread is started, and
* the descriptors closed after it is killed. So, it can't see them change.
* On the UML side, they are changed under the sigio_lock.
*/
static int write_sigio_fds[2] = { -1, -1 };
static int sigio_private[2] = { -1, -1 };
struct pollfds {
struct pollfd *poll;
int size;
int used;
};
/* Protected by sigio_lock(). Used by the sigio thread, but the UML thread
* synchronizes with it.
*/
struct pollfds current_poll = {
.poll = NULL,
.size = 0,
.used = 0
};
struct pollfds next_poll = {
.poll = NULL,
.size = 0,
.used = 0
};
static int write_sigio_thread(void *unused)
{
struct pollfds *fds, tmp;
struct pollfd *p;
int i, n, respond_fd;
char c;
fds = &current_poll;
while(1){
n = poll(fds->poll, fds->used, -1);
if(n < 0){
if(errno == EINTR) continue;
printk("write_sigio_thread : poll returned %d, "
"errno = %d\n", n, errno);
}
for(i = 0; i < fds->used; i++){
p = &fds->poll[i];
if(p->revents == 0) continue;
if(p->fd == sigio_private[1]){
n = os_read_file(sigio_private[1], &c, sizeof(c));
if(n != sizeof(c))
printk("write_sigio_thread : "
"read failed, err = %d\n", -n);
tmp = current_poll;
current_poll = next_poll;
next_poll = tmp;
respond_fd = sigio_private[1];
}
else {
respond_fd = write_sigio_fds[1];
fds->used--;
memmove(&fds->poll[i], &fds->poll[i + 1],
(fds->used - i) * sizeof(*fds->poll));
}
n = os_write_file(respond_fd, &c, sizeof(c));
if(n != sizeof(c))
printk("write_sigio_thread : write failed, "
"err = %d\n", -n);
}
}
}
static int need_poll(int n)
{
if(n <= next_poll.size){
next_poll.used = n;
return(0);
}
if(next_poll.poll != NULL) kfree(next_poll.poll);
next_poll.poll = um_kmalloc_atomic(n * sizeof(struct pollfd));
if(next_poll.poll == NULL){
printk("need_poll : failed to allocate new pollfds\n");
next_poll.size = 0;
next_poll.used = 0;
return(-1);
}
next_poll.size = n;
next_poll.used = n;
return(0);
}
/* Must be called with sigio_lock held, because it's needed by the marked
* critical section. */
static void update_thread(void)
{
unsigned long flags;
int n;
char c;
flags = set_signals(0);
n = os_write_file(sigio_private[0], &c, sizeof(c));
if(n != sizeof(c)){
printk("update_thread : write failed, err = %d\n", -n);
goto fail;
}
n = os_read_file(sigio_private[0], &c, sizeof(c));
if(n != sizeof(c)){
printk("update_thread : read failed, err = %d\n", -n);
goto fail;
}
set_signals(flags);
return;
fail:
/* Critical section start */
if(write_sigio_pid != -1)
os_kill_process(write_sigio_pid, 1);
write_sigio_pid = -1;
os_close_file(sigio_private[0]);
os_close_file(sigio_private[1]);
os_close_file(write_sigio_fds[0]);
os_close_file(write_sigio_fds[1]);
/* Critical section end */
set_signals(flags);
}
int add_sigio_fd(int fd, int read)
{
int err = 0, i, n, events;
sigio_lock();
for(i = 0; i < current_poll.used; i++){
if(current_poll.poll[i].fd == fd)
goto out;
}
n = current_poll.used + 1;
err = need_poll(n);
if(err)
goto out;
for(i = 0; i < current_poll.used; i++)
next_poll.poll[i] = current_poll.poll[i];
if(read) events = POLLIN;
else events = POLLOUT;
next_poll.poll[n - 1] = ((struct pollfd) { .fd = fd,
.events = events,
.revents = 0 });
update_thread();
out:
sigio_unlock();
return(err);
}
int ignore_sigio_fd(int fd)
{
struct pollfd *p;
int err = 0, i, n = 0;
sigio_lock();
for(i = 0; i < current_poll.used; i++){
if(current_poll.poll[i].fd == fd) break;
}
if(i == current_poll.used)
goto out;
err = need_poll(current_poll.used - 1);
if(err)
goto out;
for(i = 0; i < current_poll.used; i++){
p = &current_poll.poll[i];
if(p->fd != fd) next_poll.poll[n++] = current_poll.poll[i];
}
if(n == i){
printk("ignore_sigio_fd : fd %d not found\n", fd);
err = -1;
goto out;
}
update_thread();
out:
sigio_unlock();
return(err);
}
static int setup_initial_poll(int fd)
{
struct pollfd *p;
p = um_kmalloc(sizeof(struct pollfd));
if(p == NULL){
printk("setup_initial_poll : failed to allocate poll\n");
return(-1);
}
*p = ((struct pollfd) { .fd = fd,
.events = POLLIN,
.revents = 0 });
current_poll = ((struct pollfds) { .poll = p,
.used = 1,
.size = 1 });
return(0);
}
void write_sigio_workaround(void)
{
unsigned long stack;
int err;
sigio_lock();
if(write_sigio_pid != -1)
goto out;
err = os_pipe(write_sigio_fds, 1, 1);
if(err < 0){
printk("write_sigio_workaround - os_pipe 1 failed, "
"err = %d\n", -err);
goto out;
}
err = os_pipe(sigio_private, 1, 1);
if(err < 0){
printk("write_sigio_workaround - os_pipe 2 failed, "
"err = %d\n", -err);
goto out_close1;
}
if(setup_initial_poll(sigio_private[1]))
goto out_close2;
write_sigio_pid = run_helper_thread(write_sigio_thread, NULL,
CLONE_FILES | CLONE_VM, &stack, 0);
if(write_sigio_pid < 0) goto out_close2;
if(write_sigio_irq(write_sigio_fds[0]))
goto out_kill;
out:
sigio_unlock();
return;
out_kill:
os_kill_process(write_sigio_pid, 1);
write_sigio_pid = -1;
out_close2:
os_close_file(sigio_private[0]);
os_close_file(sigio_private[1]);
out_close1:
os_close_file(write_sigio_fds[0]);
os_close_file(write_sigio_fds[1]);
sigio_unlock();
}
int read_sigio_fd(int fd)
{
int n;
char c;
n = os_read_file(fd, &c, sizeof(c));
if(n != sizeof(c)){
if(n < 0) {
printk("read_sigio_fd - read failed, err = %d\n", -n);
return(n);
}
else {
printk("read_sigio_fd - short read, bytes = %d\n", n);
return(-EIO);
}
}
return(n);
}
static void sigio_cleanup(void)
{
if (write_sigio_pid != -1) {
os_kill_process(write_sigio_pid, 1);
write_sigio_pid = -1;
}
}
__uml_exitcall(sigio_cleanup);

213
arch/um/kernel/signal_kern.c 普通文件
查看文件

@@ -0,0 +1,213 @@
/*
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/config.h"
#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 "signal_user.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(&current->sighand->siglock);
current->blocked = *oldset;
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
force_sigsegv(signr, current);
}
else if(!(ka->sa.sa_flags & SA_NODEFER)){
spin_lock_irq(&current->sighand->siglock);
sigorsets(&current->blocked, &current->blocked,
&ka->sa.sa_mask);
sigaddset(&current->blocked, signr);
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
}
return err;
}
static int kern_do_signal(struct pt_regs *regs, sigset_t *oldset)
{
struct k_sigaction ka_copy;
siginfo_t info;
int sig, handled_sig = 0;
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))
break;
}
/* Did we come from a system call? */
if(!handled_sig && (PT_REGS_SYSCALL_NR(regs) >= 0)){
/* Restart the system call - no handlers present */
if(PT_REGS_SYSCALL_RET(regs) == -ERESTARTNOHAND ||
PT_REGS_SYSCALL_RET(regs) == -ERESTARTSYS ||
PT_REGS_SYSCALL_RET(regs) == -ERESTARTNOINTR){
PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs);
PT_REGS_RESTART_SYSCALL(regs);
}
else if(PT_REGS_SYSCALL_RET(regs) == -ERESTART_RESTARTBLOCK){
PT_REGS_SYSCALL_RET(regs) = __NR_restart_syscall;
PT_REGS_RESTART_SYSCALL(regs);
}
}
/* 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(&current->thread.regs));
return(handled_sig);
}
int do_signal(void)
{
return(kern_do_signal(&current->thread.regs, &current->blocked));
}
/*
* Atomically swap in the new signal mask, and wait for a signal.
*/
long sys_sigsuspend(int history0, int history1, old_sigset_t mask)
{
sigset_t saveset;
mask &= _BLOCKABLE;
spin_lock_irq(&current->sighand->siglock);
saveset = current->blocked;
siginitset(&current->blocked, mask);
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
PT_REGS_SYSCALL_RET(&current->thread.regs) = -EINTR;
while (1) {
current->state = TASK_INTERRUPTIBLE;
schedule();
if(kern_do_signal(&current->thread.regs, &saveset))
return(-EINTR);
}
}
long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize)
{
sigset_t saveset, newset;
/* XXX: Don't preclude handling different sized sigset_t's. */
if (sigsetsize != sizeof(sigset_t))
return -EINVAL;
if (copy_from_user(&newset, unewset, sizeof(newset)))
return -EFAULT;
sigdelsetmask(&newset, ~_BLOCKABLE);
spin_lock_irq(&current->sighand->siglock);
saveset = current->blocked;
current->blocked = newset;
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
PT_REGS_SYSCALL_RET(&current->thread.regs) = -EINTR;
while (1) {
current->state = TASK_INTERRUPTIBLE;
schedule();
if (kern_do_signal(&current->thread.regs, &saveset))
return(-EINTR);
}
}
long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss)
{
return(do_sigaltstack(uss, uoss, PT_REGS_SP(&current->thread.regs)));
}
/*
* 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:
*/

157
arch/um/kernel/signal_user.c 普通文件
查看文件

@@ -0,0 +1,157 @@
/*
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <stdarg.h>
#include <string.h>
#include <sys/mman.h>
#include "user_util.h"
#include "kern_util.h"
#include "user.h"
#include "signal_user.h"
#include "signal_kern.h"
#include "sysdep/sigcontext.h"
#include "sigcontext.h"
void set_sigstack(void *sig_stack, int size)
{
stack_t stack = ((stack_t) { .ss_flags = 0,
.ss_sp = (__ptr_t) sig_stack,
.ss_size = size - sizeof(void *) });
if(sigaltstack(&stack, NULL) != 0)
panic("enabling signal stack failed, errno = %d\n", errno);
}
void set_handler(int sig, void (*handler)(int), int flags, ...)
{
struct sigaction action;
va_list ap;
int mask;
va_start(ap, flags);
action.sa_handler = handler;
sigemptyset(&action.sa_mask);
while((mask = va_arg(ap, int)) != -1){
sigaddset(&action.sa_mask, mask);
}
va_end(ap);
action.sa_flags = flags;
action.sa_restorer = NULL;
if(sigaction(sig, &action, NULL) < 0)
panic("sigaction failed");
}
int change_sig(int signal, int on)
{
sigset_t sigset, old;
sigemptyset(&sigset);
sigaddset(&sigset, signal);
sigprocmask(on ? SIG_UNBLOCK : SIG_BLOCK, &sigset, &old);
return(!sigismember(&old, signal));
}
/* Both here and in set/get_signal we don't touch SIGPROF, because we must not
* disable profiling; it's safe because the profiling code does not interact
* with the kernel code at all.*/
static void change_signals(int type)
{
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGVTALRM);
sigaddset(&mask, SIGALRM);
sigaddset(&mask, SIGIO);
if(sigprocmask(type, &mask, NULL) < 0)
panic("Failed to change signal mask - errno = %d", errno);
}
void block_signals(void)
{
change_signals(SIG_BLOCK);
}
void unblock_signals(void)
{
change_signals(SIG_UNBLOCK);
}
/* These are the asynchronous signals. SIGVTALRM and SIGARLM are handled
* together under SIGVTALRM_BIT. SIGPROF is excluded because we want to
* be able to profile all of UML, not just the non-critical sections. If
* profiling is not thread-safe, then that is not my problem. We can disable
* profiling when SMP is enabled in that case.
*/
#define SIGIO_BIT 0
#define SIGVTALRM_BIT 1
static int enable_mask(sigset_t *mask)
{
int sigs;
sigs = sigismember(mask, SIGIO) ? 0 : 1 << SIGIO_BIT;
sigs |= sigismember(mask, SIGVTALRM) ? 0 : 1 << SIGVTALRM_BIT;
sigs |= sigismember(mask, SIGALRM) ? 0 : 1 << SIGVTALRM_BIT;
return(sigs);
}
int get_signals(void)
{
sigset_t mask;
if(sigprocmask(SIG_SETMASK, NULL, &mask) < 0)
panic("Failed to get signal mask");
return(enable_mask(&mask));
}
int set_signals(int enable)
{
sigset_t mask;
int ret;
sigemptyset(&mask);
if(enable & (1 << SIGIO_BIT))
sigaddset(&mask, SIGIO);
if(enable & (1 << SIGVTALRM_BIT)){
sigaddset(&mask, SIGVTALRM);
sigaddset(&mask, SIGALRM);
}
/* This is safe - sigprocmask is guaranteed to copy locally the
* value of new_set, do his work and then, at the end, write to
* old_set.
*/
if(sigprocmask(SIG_UNBLOCK, &mask, &mask) < 0)
panic("Failed to enable signals");
ret = enable_mask(&mask);
sigemptyset(&mask);
if((enable & (1 << SIGIO_BIT)) == 0)
sigaddset(&mask, SIGIO);
if((enable & (1 << SIGVTALRM_BIT)) == 0){
sigaddset(&mask, SIGVTALRM);
sigaddset(&mask, SIGALRM);
}
if(sigprocmask(SIG_BLOCK, &mask, NULL) < 0)
panic("Failed to block signals");
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:
*/

查看文件

@@ -0,0 +1,13 @@
#
# Copyright (C) 2002 - 2004 Jeff Dike (jdike@addtoit.com)
# Licensed under the GPL
#
obj-y := exec_kern.o mem.o mem_user.o mmu.o process.o process_kern.o \
syscall_kern.o syscall_user.o time.o tlb.o trap_user.o uaccess.o \
subdir- := util
USER_OBJS := process.o time.o
include arch/um/scripts/Makefile.rules

查看文件

@@ -0,0 +1,41 @@
/*
* 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(current->mm->context.skas.mm_fd);
}
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;
}
/*
* 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:
*/

查看文件

@@ -0,0 +1,24 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#ifndef __SKAS_MMU_H
#define __SKAS_MMU_H
struct mmu_context_skas {
int mm_fd;
};
#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:
*/

查看文件

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#ifndef __MODE_SKAS_H__
#define __MODE_SKAS_H__
#include <sysdep/ptrace.h>
extern unsigned long exec_regs[];
extern unsigned long exec_fp_regs[];
extern unsigned long exec_fpx_regs[];
extern int have_fpx_regs;
extern void user_time_init_skas(void);
extern void sig_handler_common_skas(int sig, void *sc_ptr);
extern void halt_skas(void);
extern void reboot_skas(void);
extern void kill_off_processes_skas(void);
extern int is_skas_winch(int pid, int fd, void *data);
#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:
*/

查看文件

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#ifndef __SKAS_MODE_KERN_H__
#define __SKAS_MODE_KERN_H__
#include "linux/sched.h"
#include "asm/page.h"
#include "asm/ptrace.h"
extern void flush_thread_skas(void);
extern void *switch_to_skas(void *prev, void *next);
extern void start_thread_skas(struct pt_regs *regs, unsigned long eip,
unsigned long esp);
extern 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);
extern void release_thread_skas(struct task_struct *task);
extern void exit_thread_skas(void);
extern void initial_thread_cb_skas(void (*proc)(void *), void *arg);
extern void init_idle_skas(void);
extern void flush_tlb_kernel_range_skas(unsigned long start,
unsigned long end);
extern void flush_tlb_kernel_vm_skas(void);
extern void __flush_tlb_one_skas(unsigned long addr);
extern void flush_tlb_range_skas(struct vm_area_struct *vma,
unsigned long start, unsigned long end);
extern void flush_tlb_mm_skas(struct mm_struct *mm);
extern void force_flush_all_skas(void);
extern long execute_syscall_skas(void *r);
extern void before_mem_skas(unsigned long unused);
extern unsigned long set_task_sizes_skas(int arg, unsigned long *host_size_out,
unsigned long *task_size_out);
extern int start_uml_skas(void);
extern int external_pid_skas(struct task_struct *task);
extern int thread_pid_skas(struct task_struct *task);
#define kmem_end_skas (host_task_size - 1024 * 1024)
#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:
*/

查看文件

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#ifndef __SKAS_PROC_MM_H
#define __SKAS_PROC_MM_H
#define MM_MMAP 54
#define MM_MUNMAP 55
#define MM_MPROTECT 56
#define MM_COPY_SEGMENTS 57
struct mm_mmap {
unsigned long addr;
unsigned long len;
unsigned long prot;
unsigned long flags;
unsigned long fd;
unsigned long offset;
};
struct mm_munmap {
unsigned long addr;
unsigned long len;
};
struct mm_mprotect {
unsigned long addr;
unsigned long len;
unsigned int prot;
};
struct proc_mm_op {
int op;
union {
struct mm_mmap mmap;
struct mm_munmap munmap;
struct mm_mprotect mprotect;
int copy_segments;
} u;
};
#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:
*/

查看文件

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#ifndef __SKAS_H
#define __SKAS_H
#include "sysdep/ptrace.h"
extern int userspace_pid[];
extern void switch_threads(void *me, void *next);
extern void thread_wait(void *sw, void *fb);
extern void new_thread(void *stack, void **switch_buf_ptr, void **fork_buf_ptr,
void (*handler)(int));
extern int start_idle_thread(void *stack, void *switch_buf_ptr,
void **fork_buf_ptr);
extern int user_thread(unsigned long stack, int flags);
extern void userspace(union uml_pt_regs *regs);
extern void new_thread_proc(void *stack, void (*handler)(int sig));
extern void remove_sigstack(void);
extern void new_thread_handler(int sig);
extern void handle_syscall(union uml_pt_regs *regs);
extern void map(int fd, unsigned long virt, unsigned long len, int r, int w,
int x, int phys_fd, unsigned long long offset);
extern int unmap(int fd, void *addr, unsigned long len);
extern int protect(int fd, unsigned long addr, unsigned long len,
int r, int w, int x);
extern void user_signal(int sig, union uml_pt_regs *regs);
extern int new_mm(int from);
extern void start_userspace(int cpu);
extern long execute_syscall_skas(void *r);
#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:
*/

查看文件

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#ifndef __SKAS_UACCESS_H
#define __SKAS_UACCESS_H
#include "asm/errno.h"
#include "asm/fixmap.h"
#define access_ok_skas(type, addr, size) \
((segment_eq(get_fs(), KERNEL_DS)) || \
(((unsigned long) (addr) < TASK_SIZE) && \
((unsigned long) (addr) + (size) <= TASK_SIZE)) || \
((type == VERIFY_READ ) && \
((unsigned long) (addr) >= FIXADDR_USER_START) && \
((unsigned long) (addr) + (size) <= FIXADDR_USER_END) && \
((unsigned long) (addr) + (size) >= (unsigned long)(addr))))
static inline int verify_area_skas(int type, const void * addr,
unsigned long size)
{
return(access_ok_skas(type, addr, size) ? 0 : -EFAULT);
}
extern int copy_from_user_skas(void *to, const void *from, int n);
extern int copy_to_user_skas(void *to, const void *from, int n);
extern int strncpy_from_user_skas(char *dst, const char *src, int count);
extern int __clear_user_skas(void *mem, int len);
extern int clear_user_skas(void *mem, int len);
extern int strnlen_user_skas(const void *str, int len);
#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:
*/

35
arch/um/kernel/skas/mem.c 普通文件
查看文件

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/config.h"
#include "linux/mm.h"
#include "mem_user.h"
unsigned long set_task_sizes_skas(int arg, unsigned long *host_size_out,
unsigned long *task_size_out)
{
/* Round up to the nearest 4M */
unsigned long top = ROUND_4M((unsigned long) &arg);
#ifdef CONFIG_HOST_TASK_SIZE
*host_size_out = CONFIG_HOST_TASK_SIZE;
*task_size_out = CONFIG_HOST_TASK_SIZE;
#else
*host_size_out = top;
*task_size_out = top;
#endif
return(((unsigned long) set_task_sizes_skas) & ~0xffffff);
}
/*
* 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:
*/

查看文件

@@ -0,0 +1,102 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <errno.h>
#include <sys/mman.h>
#include "mem_user.h"
#include "mem.h"
#include "user.h"
#include "os.h"
#include "proc_mm.h"
void map(int fd, unsigned long virt, unsigned long len, int r, int w,
int x, int phys_fd, unsigned long long offset)
{
struct proc_mm_op map;
int prot, n;
prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) |
(x ? PROT_EXEC : 0);
map = ((struct proc_mm_op) { .op = MM_MMAP,
.u =
{ .mmap =
{ .addr = virt,
.len = len,
.prot = prot,
.flags = MAP_SHARED |
MAP_FIXED,
.fd = phys_fd,
.offset = offset
} } } );
n = os_write_file(fd, &map, sizeof(map));
if(n != sizeof(map))
printk("map : /proc/mm map failed, err = %d\n", -n);
}
int unmap(int fd, void *addr, unsigned long len)
{
struct proc_mm_op unmap;
int n;
unmap = ((struct proc_mm_op) { .op = MM_MUNMAP,
.u =
{ .munmap =
{ .addr = (unsigned long) addr,
.len = len } } } );
n = os_write_file(fd, &unmap, sizeof(unmap));
if(n != sizeof(unmap)) {
if(n < 0)
return(n);
else if(n > 0)
return(-EIO);
}
return(0);
}
int protect(int fd, unsigned long addr, unsigned long len, int r, int w,
int x, int must_succeed)
{
struct proc_mm_op protect;
int prot, n;
prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) |
(x ? PROT_EXEC : 0);
protect = ((struct proc_mm_op) { .op = MM_MPROTECT,
.u =
{ .mprotect =
{ .addr = (unsigned long) addr,
.len = len,
.prot = prot } } } );
n = os_write_file(fd, &protect, sizeof(protect));
if(n != sizeof(protect)) {
if(n == 0) return(0);
if(must_succeed)
panic("protect failed, err = %d", -n);
return(-EIO);
}
return(0);
}
void before_mem_skas(unsigned long unused)
{
}
/*
* 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:
*/

48
arch/um/kernel/skas/mmu.c 普通文件
查看文件

@@ -0,0 +1,48 @@
/*
* 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 "asm/current.h"
#include "asm/segment.h"
#include "asm/mmu.h"
#include "os.h"
#include "skas.h"
int init_new_context_skas(struct task_struct *task, struct mm_struct *mm)
{
int from;
if((current->mm != NULL) && (current->mm != &init_mm))
from = current->mm->context.skas.mm_fd;
else from = -1;
mm->context.skas.mm_fd = new_mm(from);
if(mm->context.skas.mm_fd < 0){
printk("init_new_context_skas - new_mm failed, errno = %d\n",
mm->context.skas.mm_fd);
return(mm->context.skas.mm_fd);
}
return(0);
}
void destroy_context_skas(struct mm_struct *mm)
{
os_close_file(mm->context.skas.mm_fd);
}
/*
* 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:
*/

查看文件

@@ -0,0 +1,339 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <setjmp.h>
#include <sched.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/user.h>
#include <asm/unistd.h>
#include "user.h"
#include "ptrace_user.h"
#include "time_user.h"
#include "sysdep/ptrace.h"
#include "user_util.h"
#include "kern_util.h"
#include "skas.h"
#include "sysdep/sigcontext.h"
#include "os.h"
#include "proc_mm.h"
#include "skas_ptrace.h"
#include "chan_user.h"
#include "signal_user.h"
#include "registers.h"
int is_skas_winch(int pid, int fd, void *data)
{
if(pid != os_getpid())
return(0);
register_winch_irq(-1, fd, -1, data);
return(1);
}
static void handle_segv(int pid)
{
struct ptrace_faultinfo fault;
int err;
err = ptrace(PTRACE_FAULTINFO, pid, 0, &fault);
if(err)
panic("handle_segv - PTRACE_FAULTINFO failed, errno = %d\n",
errno);
segv(fault.addr, 0, FAULT_WRITE(fault.is_write), 1, NULL);
}
/*To use the same value of using_sysemu as the caller, ask it that value (in local_using_sysemu)*/
static void handle_trap(int pid, union uml_pt_regs *regs, int local_using_sysemu)
{
int err, status;
/* Mark this as a syscall */
UPT_SYSCALL_NR(regs) = PT_SYSCALL_NR(regs->skas.regs);
if (!local_using_sysemu)
{
err = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET, __NR_getpid);
if(err < 0)
panic("handle_trap - nullifying syscall failed errno = %d\n",
errno);
err = ptrace(PTRACE_SYSCALL, pid, 0, 0);
if(err < 0)
panic("handle_trap - continuing to end of syscall failed, "
"errno = %d\n", errno);
CATCH_EINTR(err = waitpid(pid, &status, WUNTRACED));
if((err < 0) || !WIFSTOPPED(status) ||
(WSTOPSIG(status) != SIGTRAP + 0x80))
panic("handle_trap - failed to wait at end of syscall, "
"errno = %d, status = %d\n", errno, status);
}
handle_syscall(regs);
}
static int userspace_tramp(void *arg)
{
init_new_thread_signals(0);
enable_timer();
ptrace(PTRACE_TRACEME, 0, 0, 0);
os_stop_process(os_getpid());
return(0);
}
/* Each element set once, and only accessed by a single processor anyway */
#undef NR_CPUS
#define NR_CPUS 1
int userspace_pid[NR_CPUS];
void start_userspace(int cpu)
{
void *stack;
unsigned long sp;
int pid, status, n;
stack = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if(stack == MAP_FAILED)
panic("start_userspace : mmap failed, errno = %d", errno);
sp = (unsigned long) stack + PAGE_SIZE - sizeof(void *);
pid = clone(userspace_tramp, (void *) sp,
CLONE_FILES | CLONE_VM | SIGCHLD, NULL);
if(pid < 0)
panic("start_userspace : clone failed, errno = %d", errno);
do {
CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
if(n < 0)
panic("start_userspace : wait failed, errno = %d",
errno);
} while(WIFSTOPPED(status) && (WSTOPSIG(status) == SIGVTALRM));
if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP))
panic("start_userspace : expected SIGSTOP, got status = %d",
status);
if (ptrace(PTRACE_OLDSETOPTIONS, pid, NULL, (void *)PTRACE_O_TRACESYSGOOD) < 0)
panic("start_userspace : PTRACE_SETOPTIONS failed, errno=%d\n",
errno);
if(munmap(stack, PAGE_SIZE) < 0)
panic("start_userspace : munmap failed, errno = %d\n", errno);
userspace_pid[cpu] = pid;
}
void userspace(union uml_pt_regs *regs)
{
int err, status, op, pid = userspace_pid[0];
int local_using_sysemu; /*To prevent races if using_sysemu changes under us.*/
while(1){
restore_registers(pid, regs);
/* Now we set local_using_sysemu to be used for one loop */
local_using_sysemu = get_using_sysemu();
op = SELECT_PTRACE_OPERATION(local_using_sysemu, singlestepping(NULL));
err = ptrace(op, pid, 0, 0);
if(err)
panic("userspace - could not resume userspace process, "
"pid=%d, ptrace operation = %d, errno = %d\n",
op, errno);
CATCH_EINTR(err = waitpid(pid, &status, WUNTRACED));
if(err < 0)
panic("userspace - waitpid failed, errno = %d\n",
errno);
regs->skas.is_user = 1;
save_registers(pid, regs);
UPT_SYSCALL_NR(regs) = -1; /* Assume: It's not a syscall */
if(WIFSTOPPED(status)){
switch(WSTOPSIG(status)){
case SIGSEGV:
handle_segv(pid);
break;
case SIGTRAP + 0x80:
handle_trap(pid, regs, local_using_sysemu);
break;
case SIGTRAP:
relay_signal(SIGTRAP, regs);
break;
case SIGIO:
case SIGVTALRM:
case SIGILL:
case SIGBUS:
case SIGFPE:
case SIGWINCH:
user_signal(WSTOPSIG(status), regs);
break;
default:
printk("userspace - child stopped with signal "
"%d\n", WSTOPSIG(status));
}
interrupt_end();
/* Avoid -ERESTARTSYS handling in host */
PT_SYSCALL_NR(regs->skas.regs) = -1;
}
}
}
void new_thread(void *stack, void **switch_buf_ptr, void **fork_buf_ptr,
void (*handler)(int))
{
unsigned long flags;
sigjmp_buf switch_buf, fork_buf;
*switch_buf_ptr = &switch_buf;
*fork_buf_ptr = &fork_buf;
/* Somewhat subtle - siglongjmp restores the signal mask before doing
* the longjmp. This means that when jumping from one stack to another
* when the target stack has interrupts enabled, an interrupt may occur
* on the source stack. This is bad when starting up a process because
* it's not supposed to get timer ticks until it has been scheduled.
* So, we disable interrupts around the sigsetjmp to ensure that
* they can't happen until we get back here where they are safe.
*/
flags = get_signals();
block_signals();
if(sigsetjmp(fork_buf, 1) == 0)
new_thread_proc(stack, handler);
remove_sigstack();
set_signals(flags);
}
void thread_wait(void *sw, void *fb)
{
sigjmp_buf buf, **switch_buf = sw, *fork_buf;
*switch_buf = &buf;
fork_buf = fb;
if(sigsetjmp(buf, 1) == 0)
siglongjmp(*fork_buf, 1);
}
void switch_threads(void *me, void *next)
{
sigjmp_buf my_buf, **me_ptr = me, *next_buf = next;
*me_ptr = &my_buf;
if(sigsetjmp(my_buf, 1) == 0)
siglongjmp(*next_buf, 1);
}
static sigjmp_buf initial_jmpbuf;
/* XXX Make these percpu */
static void (*cb_proc)(void *arg);
static void *cb_arg;
static sigjmp_buf *cb_back;
int start_idle_thread(void *stack, void *switch_buf_ptr, void **fork_buf_ptr)
{
sigjmp_buf **switch_buf = switch_buf_ptr;
int n;
*fork_buf_ptr = &initial_jmpbuf;
n = sigsetjmp(initial_jmpbuf, 1);
if(n == 0)
new_thread_proc((void *) stack, new_thread_handler);
else if(n == 1)
remove_sigstack();
else if(n == 2){
(*cb_proc)(cb_arg);
siglongjmp(*cb_back, 1);
}
else if(n == 3){
kmalloc_ok = 0;
return(0);
}
else if(n == 4){
kmalloc_ok = 0;
return(1);
}
siglongjmp(**switch_buf, 1);
}
void remove_sigstack(void)
{
stack_t stack = ((stack_t) { .ss_flags = SS_DISABLE,
.ss_sp = NULL,
.ss_size = 0 });
if(sigaltstack(&stack, NULL) != 0)
panic("disabling signal stack failed, errno = %d\n", errno);
}
void initial_thread_cb_skas(void (*proc)(void *), void *arg)
{
sigjmp_buf here;
cb_proc = proc;
cb_arg = arg;
cb_back = &here;
block_signals();
if(sigsetjmp(here, 1) == 0)
siglongjmp(initial_jmpbuf, 2);
unblock_signals();
cb_proc = NULL;
cb_arg = NULL;
cb_back = NULL;
}
void halt_skas(void)
{
block_signals();
siglongjmp(initial_jmpbuf, 3);
}
void reboot_skas(void)
{
block_signals();
siglongjmp(initial_jmpbuf, 4);
}
void switch_mm_skas(int mm_fd)
{
int err;
#warning need cpu pid in switch_mm_skas
err = ptrace(PTRACE_SWITCH_MM, userspace_pid[0], 0, mm_fd);
if(err)
panic("switch_mm_skas - PTRACE_SWITCH_MM failed, errno = %d\n",
errno);
}
void kill_off_processes_skas(void)
{
#warning need to loop over userspace_pids in kill_off_processes_skas
os_kill_ptraced_process(userspace_pid[0], 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:
*/

查看文件

@@ -0,0 +1,213 @@
/*
* 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 "time_user.h"
#include "signal_user.h"
#include "skas.h"
#include "os.h"
#include "user_util.h"
#include "tlb.h"
#include "kern.h"
#include "mode.h"
#include "proc_mm.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);
to->thread.prev_sched = from;
set_current(to);
switch_threads(&from->thread.mode.skas.switch_buf,
to->thread.mode.skas.switch_buf);
if(current->pid == 0)
switch_timers(1);
return(current->thread.prev_sched);
}
extern void schedule_tail(struct task_struct *prev);
void new_thread_handler(int sig)
{
int (*fn)(void *), n;
void *arg;
fn = current->thread.request.u.thread.proc;
arg = current->thread.request.u.thread.arg;
change_sig(SIGUSR1, 1);
thread_wait(&current->thread.mode.skas.switch_buf,
current->thread.mode.skas.fork_buf);
if(current->thread.prev_sched != NULL)
schedule_tail(current->thread.prev_sched);
current->thread.prev_sched = NULL;
/* The return value is 1 if the kernel thread execs a process,
* 0 if it just exits
*/
n = run_kernel_thread(fn, arg, &current->thread.exec_buf);
if(n == 1)
userspace(&current->thread.regs.regs);
else do_exit(0);
}
void new_thread_proc(void *stack, void (*handler)(int sig))
{
init_new_thread_stack(stack, handler);
os_usr1_process(os_getpid());
}
void release_thread_skas(struct task_struct *task)
{
}
void exit_thread_skas(void)
{
}
void fork_handler(int sig)
{
change_sig(SIGUSR1, 1);
thread_wait(&current->thread.mode.skas.switch_buf,
current->thread.mode.skas.fork_buf);
force_flush_all();
if(current->thread.prev_sched == NULL)
panic("blech");
schedule_tail(current->thread.prev_sched);
current->thread.prev_sched = NULL;
userspace(&current->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)(int);
if(current->thread.forking){
memcpy(&p->thread.regs.regs.skas,
&current->thread.regs.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;
}
else {
init_thread_registers(&p->thread.regs.regs);
p->thread.request.u.thread = current->thread.request.u.thread;
handler = new_thread_handler;
}
new_thread(p->thread_info, &p->thread.mode.skas.switch_buf,
&p->thread.mode.skas.fork_buf, handler);
return(0);
}
int new_mm(int from)
{
struct proc_mm_op copy;
int n, fd;
fd = os_open_file("/proc/mm", of_cloexec(of_write(OPENFLAGS())), 0);
if(fd < 0)
return(fd);
if(from != -1){
copy = ((struct proc_mm_op) { .op = MM_COPY_SEGMENTS,
.u =
{ .copy_segments = from } } );
n = os_write_file(fd, &copy, sizeof(copy));
if(n != sizeof(copy))
printk("new_mm : /proc/mm copy_segments failed, "
"err = %d\n", -n);
}
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);
}
int start_uml_skas(void)
{
start_userspace(0);
init_new_thread_signals(1);
uml_idle_timer();
init_task.thread.request.u.thread.proc = start_kernel_proc;
init_task.thread.request.u.thread.arg = NULL;
return(start_idle_thread(init_task.thread_info,
&init_task.thread.mode.skas.switch_buf,
&init_task.thread.mode.skas.fork_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]);
}
/*
* 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:
*/

查看文件

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.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"
extern syscall_handler_t *sys_call_table[];
long execute_syscall_skas(void *r)
{
struct pt_regs *regs = r;
long res;
int syscall;
current->thread.nsyscalls++;
nsyscalls++;
syscall = UPT_SYSCALL_NR(&regs->regs);
if((syscall >= NR_syscalls) || (syscall < 0))
res = -ENOSYS;
else res = EXECUTE_SYSCALL(syscall, regs);
return(res);
}
/*
* 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:
*/

查看文件

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <stdlib.h>
#include <signal.h>
#include "kern_util.h"
#include "uml-config.h"
#include "syscall_user.h"
#include "sysdep/ptrace.h"
#include "sysdep/sigcontext.h"
#include "skas.h"
void handle_syscall(union uml_pt_regs *regs)
{
long result;
#if UML_CONFIG_SYSCALL_DEBUG
int index;
index = record_syscall_start(UPT_SYSCALL_NR(regs));
#endif
syscall_trace(regs, 0);
result = execute_syscall_skas(regs);
REGS_SET_SYSCALL_RETURN(regs->skas.regs, result);
syscall_trace(regs, 1);
#if UML_CONFIG_SYSCALL_DEBUG
record_syscall_end(index, 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:
*/

30
arch/um/kernel/skas/time.c 普通文件
查看文件

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <sys/signal.h>
#include <sys/time.h>
#include "time_user.h"
#include "process.h"
#include "user.h"
void user_time_init_skas(void)
{
if(signal(SIGALRM, (__sighandler_t) alarm_handler) == SIG_ERR)
panic("Couldn't set SIGALRM handler");
if(signal(SIGVTALRM, (__sighandler_t) alarm_handler) == SIG_ERR)
panic("Couldn't set SIGVTALRM handler");
set_interval(ITIMER_VIRTUAL);
}
/*
* 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:
*/

85
arch/um/kernel/skas/tlb.c 普通文件
查看文件

@@ -0,0 +1,85 @@
/*
* 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 void do_ops(int fd, struct host_vm_op *ops, int last)
{
struct host_vm_op *op;
int i;
for(i = 0; i <= last; i++){
op = &ops[i];
switch(op->type){
case MMAP:
map(fd, 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);
break;
case MUNMAP:
unmap(fd, (void *) op->u.munmap.addr,
op->u.munmap.len);
break;
case MPROTECT:
protect(fd, op->u.mprotect.addr, op->u.mprotect.len,
op->u.mprotect.r, op->u.mprotect.w,
op->u.mprotect.x);
break;
default:
printk("Unknown op type %d in do_ops\n", op->type);
break;
}
}
}
static void fix_range(struct mm_struct *mm, unsigned long start_addr,
unsigned long end_addr, int force)
{
int fd = mm->context.skas.mm_fd;
fix_range_common(mm, start_addr, end_addr, force, fd, 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)
{
/* Don't bother flushing if this address space is about to be
* destroyed.
*/
if(atomic_read(&mm->mm_users) == 0)
return;
fix_range(mm, 0, host_task_size, 0);
flush_tlb_kernel_range_common(start_vm, end_vm);
}
void force_flush_all_skas(void)
{
fix_range(current->mm, 0, host_task_size, 1);
}

查看文件

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com)
* Licensed under the GPL
*/
#include <signal.h>
#include <errno.h>
#include "sysdep/ptrace.h"
#include "signal_user.h"
#include "user_util.h"
#include "kern_util.h"
#include "task.h"
#include "sigcontext.h"
void sig_handler_common_skas(int sig, void *sc_ptr)
{
struct sigcontext *sc = sc_ptr;
struct skas_regs *r;
struct signal_info *info;
int save_errno = errno;
int save_user;
/* 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.
* XXX Figure out why this is better than SA_NODEFER
*/
if(sig == SIGSEGV)
change_sig(SIGSEGV, 1);
r = &TASK_REGS(get_current())->skas;
save_user = r->is_user;
r->is_user = 0;
r->fault_addr = SC_FAULT_ADDR(sc);
r->fault_type = SC_FAULT_TYPE(sc);
r->trap_type = SC_TRAP_TYPE(sc);
change_sig(SIGUSR1, 1);
info = &sig_info[sig];
if(!info->is_irq) unblock_signals();
(*info->handler)(sig, (union uml_pt_regs *) r);
errno = save_errno;
r->is_user = save_user;
}
void user_signal(int sig, union uml_pt_regs *regs)
{
struct signal_info *info;
regs->skas.is_user = 1;
regs->skas.fault_addr = 0;
regs->skas.fault_type = 0;
regs->skas.trap_type = 0;
info = &sig_info[sig];
(*info->handler)(sig, regs);
unblock_signals();
}
/*
* 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:
*/

查看文件

@@ -0,0 +1,259 @@
/*
* Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com)
* Licensed under the GPL
*/
#include "linux/stddef.h"
#include "linux/kernel.h"
#include "linux/string.h"
#include "linux/fs.h"
#include "linux/highmem.h"
#include "asm/page.h"
#include "asm/pgtable.h"
#include "asm/uaccess.h"
#include "kern_util.h"
#include "user_util.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(0);
phys = um_virt_to_phys(current, virt, NULL);
}
return((unsigned long) phys);
}
static int do_op(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 == -1)
return(-1);
page = phys_to_page(addr);
addr = (unsigned long) kmap(page) + (addr & ~PAGE_MASK);
n = (*op)(addr, len, arg);
kunmap(page);
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;
/* Some old gccs recognize __va_copy, but not va_copy */
__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(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(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(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_skas(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_skas(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_skas(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_skas(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:
*/

查看文件

@@ -0,0 +1,4 @@
hostprogs-y := mk_ptregs
always := $(hostprogs-y)
mk_ptregs-objs := mk_ptregs-$(SUBARCH).o

查看文件

@@ -0,0 +1,51 @@
#include <stdio.h>
#include <asm/ptrace.h>
#include <asm/user.h>
#define PRINT_REG(name, val) printf("#define HOST_%s %d\n", (name), (val))
int main(int argc, char **argv)
{
printf("/* Automatically generated by "
"arch/um/kernel/skas/util/mk_ptregs */\n");
printf("\n");
printf("#ifndef __SKAS_PT_REGS_\n");
printf("#define __SKAS_PT_REGS_\n");
printf("\n");
printf("#define HOST_FRAME_SIZE %d\n", FRAME_SIZE);
printf("#define HOST_FP_SIZE %d\n",
sizeof(struct user_i387_struct) / sizeof(unsigned long));
printf("#define HOST_XFP_SIZE %d\n",
sizeof(struct user_fxsr_struct) / sizeof(unsigned long));
PRINT_REG("IP", EIP);
PRINT_REG("SP", UESP);
PRINT_REG("EFLAGS", EFL);
PRINT_REG("EAX", EAX);
PRINT_REG("EBX", EBX);
PRINT_REG("ECX", ECX);
PRINT_REG("EDX", EDX);
PRINT_REG("ESI", ESI);
PRINT_REG("EDI", EDI);
PRINT_REG("EBP", EBP);
PRINT_REG("CS", CS);
PRINT_REG("SS", SS);
PRINT_REG("DS", DS);
PRINT_REG("FS", FS);
PRINT_REG("ES", ES);
PRINT_REG("GS", GS);
printf("\n");
printf("#endif\n");
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:
*/

查看文件

@@ -0,0 +1,68 @@
/*
* Copyright 2003 PathScale, Inc.
*
* Licensed under the GPL
*/
#include <stdio.h>
#define __FRAME_OFFSETS
#include <asm/ptrace.h>
#define PRINT_REG(name, val) \
printf("#define HOST_%s (%d / sizeof(unsigned long))\n", (name), (val))
int main(int argc, char **argv)
{
printf("/* Automatically generated by "
"arch/um/kernel/skas/util/mk_ptregs */\n");
printf("\n");
printf("#ifndef __SKAS_PT_REGS_\n");
printf("#define __SKAS_PT_REGS_\n");
printf("#define HOST_FRAME_SIZE (%d / sizeof(unsigned long))\n",
FRAME_SIZE);
PRINT_REG("RBX", RBX);
PRINT_REG("RCX", RCX);
PRINT_REG("RDI", RDI);
PRINT_REG("RSI", RSI);
PRINT_REG("RDX", RDX);
PRINT_REG("RBP", RBP);
PRINT_REG("RAX", RAX);
PRINT_REG("R8", R8);
PRINT_REG("R9", R9);
PRINT_REG("R10", R10);
PRINT_REG("R11", R11);
PRINT_REG("R12", R12);
PRINT_REG("R13", R13);
PRINT_REG("R14", R14);
PRINT_REG("R15", R15);
PRINT_REG("ORIG_RAX", ORIG_RAX);
PRINT_REG("CS", CS);
PRINT_REG("SS", SS);
PRINT_REG("EFLAGS", EFLAGS);
#if 0
PRINT_REG("FS", FS);
PRINT_REG("GS", GS);
PRINT_REG("DS", DS);
PRINT_REG("ES", ES);
#endif
PRINT_REG("IP", RIP);
PRINT_REG("SP", RSP);
printf("#define HOST_FP_SIZE 0\n");
printf("#define HOST_XFP_SIZE 0\n");
printf("\n");
printf("\n");
printf("#endif\n");
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:
*/

269
arch/um/kernel/smp.c 普通文件
查看文件

@@ -0,0 +1,269 @@
/*
* Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
* Licensed under the GPL
*/
#include "linux/config.h"
#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);
activate_ipi(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);
activate_ipi(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);
unhash_process(idle);
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:
*/

查看文件

@@ -0,0 +1,276 @@
/*
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
* Copyright 2003 PathScale, Inc.
* Licensed under the GPL
*/
#include "linux/config.h"
#include "linux/unistd.h"
#include "linux/sys.h"
#include "linux/swap.h"
#include "linux/syscalls.h"
#include "linux/sysctl.h"
#include "asm/signal.h"
#include "sysdep/syscalls.h"
#include "kern_util.h"
#ifdef CONFIG_NFSD
#define NFSSERVCTL sys_nfsservctl
#else
#define NFSSERVCTL sys_ni_syscall
#endif
#define LAST_GENERIC_SYSCALL __NR_keyctl
#if LAST_GENERIC_SYSCALL > LAST_ARCH_SYSCALL
#define LAST_SYSCALL LAST_GENERIC_SYSCALL
#else
#define LAST_SYSCALL LAST_ARCH_SYSCALL
#endif
extern syscall_handler_t sys_fork;
extern syscall_handler_t sys_execve;
extern syscall_handler_t um_time;
extern syscall_handler_t um_stime;
extern syscall_handler_t sys_pipe;
extern syscall_handler_t sys_olduname;
extern syscall_handler_t sys_sigaction;
extern syscall_handler_t sys_sigsuspend;
extern syscall_handler_t old_readdir;
extern syscall_handler_t sys_uname;
extern syscall_handler_t sys_ipc;
extern syscall_handler_t sys_sigreturn;
extern syscall_handler_t sys_clone;
extern syscall_handler_t sys_rt_sigreturn;
extern syscall_handler_t sys_sigaltstack;
extern syscall_handler_t sys_vfork;
extern syscall_handler_t old_select;
extern syscall_handler_t sys_modify_ldt;
extern syscall_handler_t sys_rt_sigsuspend;
extern syscall_handler_t sys_mbind;
extern syscall_handler_t sys_get_mempolicy;
extern syscall_handler_t sys_set_mempolicy;
extern syscall_handler_t sys_sys_setaltroot;
syscall_handler_t *sys_call_table[] = {
[ __NR_restart_syscall ] = (syscall_handler_t *) sys_restart_syscall,
[ __NR_exit ] = (syscall_handler_t *) sys_exit,
[ __NR_fork ] = (syscall_handler_t *) sys_fork,
[ __NR_read ] = (syscall_handler_t *) sys_read,
[ __NR_write ] = (syscall_handler_t *) sys_write,
/* These three are declared differently in asm/unistd.h */
[ __NR_open ] = (syscall_handler_t *) sys_open,
[ __NR_close ] = (syscall_handler_t *) sys_close,
[ __NR_creat ] = (syscall_handler_t *) sys_creat,
[ __NR_link ] = (syscall_handler_t *) sys_link,
[ __NR_unlink ] = (syscall_handler_t *) sys_unlink,
[ __NR_execve ] = (syscall_handler_t *) sys_execve,
/* declared differently in kern_util.h */
[ __NR_chdir ] = (syscall_handler_t *) sys_chdir,
[ __NR_time ] = um_time,
[ __NR_mknod ] = (syscall_handler_t *) sys_mknod,
[ __NR_chmod ] = (syscall_handler_t *) sys_chmod,
[ __NR_lchown ] = (syscall_handler_t *) sys_lchown16,
[ __NR_lseek ] = (syscall_handler_t *) sys_lseek,
[ __NR_getpid ] = (syscall_handler_t *) sys_getpid,
[ __NR_mount ] = (syscall_handler_t *) sys_mount,
[ __NR_setuid ] = (syscall_handler_t *) sys_setuid16,
[ __NR_getuid ] = (syscall_handler_t *) sys_getuid16,
[ __NR_ptrace ] = (syscall_handler_t *) sys_ptrace,
[ __NR_alarm ] = (syscall_handler_t *) sys_alarm,
[ __NR_pause ] = (syscall_handler_t *) sys_pause,
[ __NR_utime ] = (syscall_handler_t *) sys_utime,
[ __NR_access ] = (syscall_handler_t *) sys_access,
[ __NR_sync ] = (syscall_handler_t *) sys_sync,
[ __NR_kill ] = (syscall_handler_t *) sys_kill,
[ __NR_rename ] = (syscall_handler_t *) sys_rename,
[ __NR_mkdir ] = (syscall_handler_t *) sys_mkdir,
[ __NR_rmdir ] = (syscall_handler_t *) sys_rmdir,
/* Declared differently in asm/unistd.h */
[ __NR_dup ] = (syscall_handler_t *) sys_dup,
[ __NR_pipe ] = (syscall_handler_t *) sys_pipe,
[ __NR_times ] = (syscall_handler_t *) sys_times,
[ __NR_brk ] = (syscall_handler_t *) sys_brk,
[ __NR_setgid ] = (syscall_handler_t *) sys_setgid16,
[ __NR_getgid ] = (syscall_handler_t *) sys_getgid16,
[ __NR_geteuid ] = (syscall_handler_t *) sys_geteuid16,
[ __NR_getegid ] = (syscall_handler_t *) sys_getegid16,
[ __NR_acct ] = (syscall_handler_t *) sys_acct,
[ __NR_umount2 ] = (syscall_handler_t *) sys_umount,
[ __NR_ioctl ] = (syscall_handler_t *) sys_ioctl,
[ __NR_fcntl ] = (syscall_handler_t *) sys_fcntl,
[ __NR_setpgid ] = (syscall_handler_t *) sys_setpgid,
[ __NR_umask ] = (syscall_handler_t *) sys_umask,
[ __NR_chroot ] = (syscall_handler_t *) sys_chroot,
[ __NR_ustat ] = (syscall_handler_t *) sys_ustat,
[ __NR_dup2 ] = (syscall_handler_t *) sys_dup2,
[ __NR_getppid ] = (syscall_handler_t *) sys_getppid,
[ __NR_getpgrp ] = (syscall_handler_t *) sys_getpgrp,
[ __NR_setsid ] = (syscall_handler_t *) sys_setsid,
[ __NR_setreuid ] = (syscall_handler_t *) sys_setreuid16,
[ __NR_setregid ] = (syscall_handler_t *) sys_setregid16,
[ __NR_sethostname ] = (syscall_handler_t *) sys_sethostname,
[ __NR_setrlimit ] = (syscall_handler_t *) sys_setrlimit,
[ __NR_getrlimit ] = (syscall_handler_t *) sys_old_getrlimit,
[ __NR_getrusage ] = (syscall_handler_t *) sys_getrusage,
[ __NR_gettimeofday ] = (syscall_handler_t *) sys_gettimeofday,
[ __NR_settimeofday ] = (syscall_handler_t *) sys_settimeofday,
[ __NR_getgroups ] = (syscall_handler_t *) sys_getgroups16,
[ __NR_setgroups ] = (syscall_handler_t *) sys_setgroups16,
[ __NR_symlink ] = (syscall_handler_t *) sys_symlink,
[ __NR_readlink ] = (syscall_handler_t *) sys_readlink,
[ __NR_uselib ] = (syscall_handler_t *) sys_uselib,
[ __NR_swapon ] = (syscall_handler_t *) sys_swapon,
[ __NR_reboot ] = (syscall_handler_t *) sys_reboot,
[ __NR_munmap ] = (syscall_handler_t *) sys_munmap,
[ __NR_truncate ] = (syscall_handler_t *) sys_truncate,
[ __NR_ftruncate ] = (syscall_handler_t *) sys_ftruncate,
[ __NR_fchmod ] = (syscall_handler_t *) sys_fchmod,
[ __NR_fchown ] = (syscall_handler_t *) sys_fchown16,
[ __NR_getpriority ] = (syscall_handler_t *) sys_getpriority,
[ __NR_setpriority ] = (syscall_handler_t *) sys_setpriority,
[ __NR_statfs ] = (syscall_handler_t *) sys_statfs,
[ __NR_fstatfs ] = (syscall_handler_t *) sys_fstatfs,
[ __NR_ioperm ] = (syscall_handler_t *) sys_ni_syscall,
[ __NR_syslog ] = (syscall_handler_t *) sys_syslog,
[ __NR_setitimer ] = (syscall_handler_t *) sys_setitimer,
[ __NR_getitimer ] = (syscall_handler_t *) sys_getitimer,
[ __NR_stat ] = (syscall_handler_t *) sys_newstat,
[ __NR_lstat ] = (syscall_handler_t *) sys_newlstat,
[ __NR_fstat ] = (syscall_handler_t *) sys_newfstat,
[ __NR_vhangup ] = (syscall_handler_t *) sys_vhangup,
[ __NR_wait4 ] = (syscall_handler_t *) sys_wait4,
[ __NR_swapoff ] = (syscall_handler_t *) sys_swapoff,
[ __NR_sysinfo ] = (syscall_handler_t *) sys_sysinfo,
[ __NR_fsync ] = (syscall_handler_t *) sys_fsync,
[ __NR_clone ] = (syscall_handler_t *) sys_clone,
[ __NR_setdomainname ] = (syscall_handler_t *) sys_setdomainname,
[ __NR_uname ] = (syscall_handler_t *) sys_newuname,
[ __NR_adjtimex ] = (syscall_handler_t *) sys_adjtimex,
[ __NR_mprotect ] = (syscall_handler_t *) sys_mprotect,
[ __NR_create_module ] = (syscall_handler_t *) sys_ni_syscall,
[ __NR_init_module ] = (syscall_handler_t *) sys_init_module,
[ __NR_delete_module ] = (syscall_handler_t *) sys_delete_module,
[ __NR_get_kernel_syms ] = (syscall_handler_t *) sys_ni_syscall,
[ __NR_quotactl ] = (syscall_handler_t *) sys_quotactl,
[ __NR_getpgid ] = (syscall_handler_t *) sys_getpgid,
[ __NR_fchdir ] = (syscall_handler_t *) sys_fchdir,
[ __NR_sysfs ] = (syscall_handler_t *) sys_sysfs,
[ __NR_personality ] = (syscall_handler_t *) sys_personality,
[ __NR_afs_syscall ] = (syscall_handler_t *) sys_ni_syscall,
[ __NR_setfsuid ] = (syscall_handler_t *) sys_setfsuid16,
[ __NR_setfsgid ] = (syscall_handler_t *) sys_setfsgid16,
[ __NR_getdents ] = (syscall_handler_t *) sys_getdents,
[ __NR_flock ] = (syscall_handler_t *) sys_flock,
[ __NR_msync ] = (syscall_handler_t *) sys_msync,
[ __NR_readv ] = (syscall_handler_t *) sys_readv,
[ __NR_writev ] = (syscall_handler_t *) sys_writev,
[ __NR_getsid ] = (syscall_handler_t *) sys_getsid,
[ __NR_fdatasync ] = (syscall_handler_t *) sys_fdatasync,
[ __NR__sysctl ] = (syscall_handler_t *) sys_sysctl,
[ __NR_mlock ] = (syscall_handler_t *) sys_mlock,
[ __NR_munlock ] = (syscall_handler_t *) sys_munlock,
[ __NR_mlockall ] = (syscall_handler_t *) sys_mlockall,
[ __NR_munlockall ] = (syscall_handler_t *) sys_munlockall,
[ __NR_sched_setparam ] = (syscall_handler_t *) sys_sched_setparam,
[ __NR_sched_getparam ] = (syscall_handler_t *) sys_sched_getparam,
[ __NR_sched_setscheduler ] = (syscall_handler_t *) sys_sched_setscheduler,
[ __NR_sched_getscheduler ] = (syscall_handler_t *) sys_sched_getscheduler,
[ __NR_sched_yield ] = (syscall_handler_t *) yield,
[ __NR_sched_get_priority_max ] = (syscall_handler_t *) sys_sched_get_priority_max,
[ __NR_sched_get_priority_min ] = (syscall_handler_t *) sys_sched_get_priority_min,
[ __NR_sched_rr_get_interval ] = (syscall_handler_t *) sys_sched_rr_get_interval,
[ __NR_nanosleep ] = (syscall_handler_t *) sys_nanosleep,
[ __NR_mremap ] = (syscall_handler_t *) sys_mremap,
[ __NR_setresuid ] = (syscall_handler_t *) sys_setresuid16,
[ __NR_getresuid ] = (syscall_handler_t *) sys_getresuid16,
[ __NR_query_module ] = (syscall_handler_t *) sys_ni_syscall,
[ __NR_poll ] = (syscall_handler_t *) sys_poll,
[ __NR_nfsservctl ] = (syscall_handler_t *) NFSSERVCTL,
[ __NR_setresgid ] = (syscall_handler_t *) sys_setresgid16,
[ __NR_getresgid ] = (syscall_handler_t *) sys_getresgid16,
[ __NR_prctl ] = (syscall_handler_t *) sys_prctl,
[ __NR_rt_sigreturn ] = (syscall_handler_t *) sys_rt_sigreturn,
[ __NR_rt_sigaction ] = (syscall_handler_t *) sys_rt_sigaction,
[ __NR_rt_sigprocmask ] = (syscall_handler_t *) sys_rt_sigprocmask,
[ __NR_rt_sigpending ] = (syscall_handler_t *) sys_rt_sigpending,
[ __NR_rt_sigtimedwait ] = (syscall_handler_t *) sys_rt_sigtimedwait,
[ __NR_rt_sigqueueinfo ] = (syscall_handler_t *) sys_rt_sigqueueinfo,
[ __NR_rt_sigsuspend ] = (syscall_handler_t *) sys_rt_sigsuspend,
[ __NR_pread64 ] = (syscall_handler_t *) sys_pread64,
[ __NR_pwrite64 ] = (syscall_handler_t *) sys_pwrite64,
[ __NR_chown ] = (syscall_handler_t *) sys_chown16,
[ __NR_getcwd ] = (syscall_handler_t *) sys_getcwd,
[ __NR_capget ] = (syscall_handler_t *) sys_capget,
[ __NR_capset ] = (syscall_handler_t *) sys_capset,
[ __NR_sigaltstack ] = (syscall_handler_t *) sys_sigaltstack,
[ __NR_sendfile ] = (syscall_handler_t *) sys_sendfile,
[ __NR_getpmsg ] = (syscall_handler_t *) sys_ni_syscall,
[ __NR_putpmsg ] = (syscall_handler_t *) sys_ni_syscall,
[ __NR_vfork ] = (syscall_handler_t *) sys_vfork,
[ __NR_getdents64 ] = (syscall_handler_t *) sys_getdents64,
[ __NR_gettid ] = (syscall_handler_t *) sys_gettid,
[ __NR_readahead ] = (syscall_handler_t *) sys_readahead,
[ __NR_setxattr ] = (syscall_handler_t *) sys_setxattr,
[ __NR_lsetxattr ] = (syscall_handler_t *) sys_lsetxattr,
[ __NR_fsetxattr ] = (syscall_handler_t *) sys_fsetxattr,
[ __NR_getxattr ] = (syscall_handler_t *) sys_getxattr,
[ __NR_lgetxattr ] = (syscall_handler_t *) sys_lgetxattr,
[ __NR_fgetxattr ] = (syscall_handler_t *) sys_fgetxattr,
[ __NR_listxattr ] = (syscall_handler_t *) sys_listxattr,
[ __NR_llistxattr ] = (syscall_handler_t *) sys_llistxattr,
[ __NR_flistxattr ] = (syscall_handler_t *) sys_flistxattr,
[ __NR_removexattr ] = (syscall_handler_t *) sys_removexattr,
[ __NR_lremovexattr ] = (syscall_handler_t *) sys_lremovexattr,
[ __NR_fremovexattr ] = (syscall_handler_t *) sys_fremovexattr,
[ __NR_tkill ] = (syscall_handler_t *) sys_tkill,
[ __NR_futex ] = (syscall_handler_t *) sys_futex,
[ __NR_sched_setaffinity ] = (syscall_handler_t *) sys_sched_setaffinity,
[ __NR_sched_getaffinity ] = (syscall_handler_t *) sys_sched_getaffinity,
[ __NR_io_setup ] = (syscall_handler_t *) sys_io_setup,
[ __NR_io_destroy ] = (syscall_handler_t *) sys_io_destroy,
[ __NR_io_getevents ] = (syscall_handler_t *) sys_io_getevents,
[ __NR_io_submit ] = (syscall_handler_t *) sys_io_submit,
[ __NR_io_cancel ] = (syscall_handler_t *) sys_io_cancel,
[ __NR_exit_group ] = (syscall_handler_t *) sys_exit_group,
[ __NR_lookup_dcookie ] = (syscall_handler_t *) sys_lookup_dcookie,
[ __NR_epoll_create ] = (syscall_handler_t *) sys_epoll_create,
[ __NR_epoll_ctl ] = (syscall_handler_t *) sys_epoll_ctl,
[ __NR_epoll_wait ] = (syscall_handler_t *) sys_epoll_wait,
[ __NR_remap_file_pages ] = (syscall_handler_t *) sys_remap_file_pages,
[ __NR_set_tid_address ] = (syscall_handler_t *) sys_set_tid_address,
[ __NR_timer_create ] = (syscall_handler_t *) sys_timer_create,
[ __NR_timer_settime ] = (syscall_handler_t *) sys_timer_settime,
[ __NR_timer_gettime ] = (syscall_handler_t *) sys_timer_gettime,
[ __NR_timer_getoverrun ] = (syscall_handler_t *) sys_timer_getoverrun,
[ __NR_timer_delete ] = (syscall_handler_t *) sys_timer_delete,
[ __NR_clock_settime ] = (syscall_handler_t *) sys_clock_settime,
[ __NR_clock_gettime ] = (syscall_handler_t *) sys_clock_gettime,
[ __NR_clock_getres ] = (syscall_handler_t *) sys_clock_getres,
[ __NR_clock_nanosleep ] = (syscall_handler_t *) sys_clock_nanosleep,
[ __NR_tgkill ] = (syscall_handler_t *) sys_tgkill,
[ __NR_utimes ] = (syscall_handler_t *) sys_utimes,
[ __NR_fadvise64 ] = (syscall_handler_t *) sys_fadvise64,
[ __NR_vserver ] = (syscall_handler_t *) sys_ni_syscall,
[ __NR_mbind ] = (syscall_handler_t *) sys_mbind,
[ __NR_get_mempolicy ] = (syscall_handler_t *) sys_get_mempolicy,
[ __NR_set_mempolicy ] = (syscall_handler_t *) sys_set_mempolicy,
[ __NR_mq_open ] = (syscall_handler_t *) sys_mq_open,
[ __NR_mq_unlink ] = (syscall_handler_t *) sys_mq_unlink,
[ __NR_mq_timedsend ] = (syscall_handler_t *) sys_mq_timedsend,
[ __NR_mq_timedreceive ] = (syscall_handler_t *) sys_mq_timedreceive,
[ __NR_mq_notify ] = (syscall_handler_t *) sys_mq_notify,
[ __NR_mq_getsetattr ] = (syscall_handler_t *) sys_mq_getsetattr,
[ __NR_kexec_load ] = (syscall_handler_t *) sys_ni_syscall,
[ __NR_waitid ] = (syscall_handler_t *) sys_waitid,
[ __NR_add_key ] = (syscall_handler_t *) sys_add_key,
[ __NR_request_key ] = (syscall_handler_t *) sys_request_key,
[ __NR_keyctl ] = (syscall_handler_t *) sys_keyctl,
ARCH_SYSCALLS
[ LAST_SYSCALL + 1 ... NR_syscalls ] =
(syscall_handler_t *) sys_ni_syscall
};

查看文件

@@ -0,0 +1,176 @@
/*
* 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 "asm/ipc.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, 0, NULL, 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, 0, NULL, 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(&current->mm->mmap_sem);
error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
up_write(&current->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 * name)
{
long err;
if (!name)
return -EFAULT;
down_read(&uts_sem);
err=copy_to_user(name, &system_utsname, sizeof (*name));
up_read(&uts_sem);
return err?-EFAULT:0;
}
long sys_olduname(struct oldold_utsname * 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,&system_utsname.sysname,
__OLD_UTS_LEN);
error |= __put_user(0,name->sysname+__OLD_UTS_LEN);
error |= __copy_to_user(&name->nodename,&system_utsname.nodename,
__OLD_UTS_LEN);
error |= __put_user(0,name->nodename+__OLD_UTS_LEN);
error |= __copy_to_user(&name->release,&system_utsname.release,
__OLD_UTS_LEN);
error |= __put_user(0,name->release+__OLD_UTS_LEN);
error |= __copy_to_user(&name->version,&system_utsname.version,
__OLD_UTS_LEN);
error |= __put_user(0,name->version+__OLD_UTS_LEN);
error |= __copy_to_user(&name->machine,&system_utsname.machine,
__OLD_UTS_LEN);
error |= __put_user(0,name->machine+__OLD_UTS_LEN);
up_read(&uts_sem);
error = error ? -EFAULT : 0;
return error;
}
DEFINE_SPINLOCK(syscall_lock);
static int syscall_index = 0;
int next_syscall_index(int limit)
{
int ret;
spin_lock(&syscall_lock);
ret = syscall_index;
if(++syscall_index == limit)
syscall_index = 0;
spin_unlock(&syscall_lock);
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:
*/

查看文件

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <stdlib.h>
#include <sys/time.h>
#include "kern_util.h"
#include "syscall_user.h"
struct {
int syscall;
int pid;
long result;
struct timeval start;
struct timeval end;
} syscall_record[1024];
int record_syscall_start(int syscall)
{
int max, index;
max = sizeof(syscall_record)/sizeof(syscall_record[0]);
index = next_syscall_index(max);
syscall_record[index].syscall = syscall;
syscall_record[index].pid = current_pid();
syscall_record[index].result = 0xdeadbeef;
gettimeofday(&syscall_record[index].start, NULL);
return(index);
}
void record_syscall_end(int index, long result)
{
syscall_record[index].result = result;
gettimeofday(&syscall_record[index].end, NULL);
}
/*
* 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:
*/

81
arch/um/kernel/sysrq.c 普通文件
查看文件

@@ -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"
void show_trace(unsigned long * stack)
{
/* XXX: Copy the CONFIG_FRAME_POINTER stack-walking backtrace from
* arch/i386/kernel/traps.c, and then move this to sys-i386/sysrq.c.*/
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");
}
/*
* 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(&stack);
}
EXPORT_SYMBOL(dump_stack);
/*Stolen from arch/i386/kernel/traps.c */
static 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) {
esp = (unsigned long *) KSTK_ESP(task);
/* Which one? No actual difference - just coding style.*/
//esp = (unsigned long *) PT_REGS_IP(&task->thread.regs);
} 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++);
}
show_trace(esp);
}

82
arch/um/kernel/tempfile.c 普通文件
查看文件

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/param.h>
#include "init.h"
/* Modified from create_mem_file and start_debugger */
static char *tempdir = NULL;
static void __init find_tempdir(void)
{
char *dirs[] = { "TMP", "TEMP", "TMPDIR", NULL };
int i;
char *dir = NULL;
if(tempdir != NULL) return; /* We've already been called */
for(i = 0; dirs[i]; i++){
dir = getenv(dirs[i]);
if((dir != NULL) && (*dir != '\0'))
break;
}
if((dir == NULL) || (*dir == '\0'))
dir = "/tmp";
tempdir = malloc(strlen(dir) + 2);
if(tempdir == NULL){
fprintf(stderr, "Failed to malloc tempdir, "
"errno = %d\n", errno);
return;
}
strcpy(tempdir, dir);
strcat(tempdir, "/");
}
int make_tempfile(const char *template, char **out_tempname, int do_unlink)
{
char tempname[MAXPATHLEN];
int fd;
find_tempdir();
if (*template != '/')
strcpy(tempname, tempdir);
else
*tempname = 0;
strcat(tempname, template);
fd = mkstemp(tempname);
if(fd < 0){
fprintf(stderr, "open - cannot create %s: %s\n", tempname,
strerror(errno));
return -1;
}
if(do_unlink && (unlink(tempname) < 0)){
perror("unlink");
return -1;
}
if(out_tempname){
*out_tempname = strdup(tempname);
if(*out_tempname == NULL){
perror("strdup");
return -1;
}
}
return(fd);
}
/*
* 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:
*/

167
arch/um/kernel/time.c 普通文件
查看文件

@@ -0,0 +1,167 @@
/*
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include "user_util.h"
#include "kern_util.h"
#include "user.h"
#include "process.h"
#include "signal_user.h"
#include "time_user.h"
#include "kern_constants.h"
/* XXX This really needs to be declared and initialized in a kernel file since
* it's in <linux/time.h>
*/
extern struct timespec wall_to_monotonic;
extern struct timeval xtime;
struct timeval local_offset = { 0, 0 };
void timer(void)
{
gettimeofday(&xtime, NULL);
timeradd(&xtime, &local_offset, &xtime);
}
void set_interval(int timer_type)
{
int usec = 1000000/hz();
struct itimerval interval = ((struct itimerval) { { 0, usec },
{ 0, usec } });
if(setitimer(timer_type, &interval, NULL) == -1)
panic("setitimer failed - errno = %d\n", errno);
}
void enable_timer(void)
{
int usec = 1000000/hz();
struct itimerval enable = ((struct itimerval) { { 0, usec },
{ 0, usec }});
if(setitimer(ITIMER_VIRTUAL, &enable, NULL))
printk("enable_timer - setitimer failed, errno = %d\n",
errno);
}
void disable_timer(void)
{
struct itimerval disable = ((struct itimerval) { { 0, 0 }, { 0, 0 }});
if((setitimer(ITIMER_VIRTUAL, &disable, NULL) < 0) ||
(setitimer(ITIMER_REAL, &disable, NULL) < 0))
printk("disnable_timer - setitimer failed, errno = %d\n",
errno);
/* If there are signals already queued, after unblocking ignore them */
set_handler(SIGALRM, SIG_IGN, 0, -1);
set_handler(SIGVTALRM, SIG_IGN, 0, -1);
}
void switch_timers(int to_real)
{
struct itimerval disable = ((struct itimerval) { { 0, 0 }, { 0, 0 }});
struct itimerval enable = ((struct itimerval) { { 0, 1000000/hz() },
{ 0, 1000000/hz() }});
int old, new;
if(to_real){
old = ITIMER_VIRTUAL;
new = ITIMER_REAL;
}
else {
old = ITIMER_REAL;
new = ITIMER_VIRTUAL;
}
if((setitimer(old, &disable, NULL) < 0) ||
(setitimer(new, &enable, NULL)))
printk("switch_timers - setitimer failed, errno = %d\n",
errno);
}
void uml_idle_timer(void)
{
if(signal(SIGVTALRM, SIG_IGN) == SIG_ERR)
panic("Couldn't unset SIGVTALRM handler");
set_handler(SIGALRM, (__sighandler_t) alarm_handler,
SA_RESTART, SIGUSR1, SIGIO, SIGWINCH, SIGVTALRM, -1);
set_interval(ITIMER_REAL);
}
extern int do_posix_clock_monotonic_gettime(struct timespec *tp);
void time_init(void)
{
struct timespec now;
if(signal(SIGVTALRM, boot_timer_handler) == SIG_ERR)
panic("Couldn't set SIGVTALRM handler");
set_interval(ITIMER_VIRTUAL);
do_posix_clock_monotonic_gettime(&now);
wall_to_monotonic.tv_sec = -now.tv_sec;
wall_to_monotonic.tv_nsec = -now.tv_nsec;
}
/* Declared in linux/time.h, which can't be included here */
extern void clock_was_set(void);
void do_gettimeofday(struct timeval *tv)
{
unsigned long flags;
flags = time_lock();
gettimeofday(tv, NULL);
timeradd(tv, &local_offset, tv);
time_unlock(flags);
clock_was_set();
}
int do_settimeofday(struct timespec *tv)
{
struct timeval now;
unsigned long flags;
struct timeval tv_in;
if ((unsigned long) tv->tv_nsec >= UM_NSEC_PER_SEC)
return -EINVAL;
tv_in.tv_sec = tv->tv_sec;
tv_in.tv_usec = tv->tv_nsec / 1000;
flags = time_lock();
gettimeofday(&now, NULL);
timersub(&tv_in, &now, &local_offset);
time_unlock(flags);
return(0);
}
void idle_sleep(int secs)
{
struct timespec ts;
ts.tv_sec = secs;
ts.tv_nsec = 0;
nanosleep(&ts, NULL);
}
/*
* 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:
*/

203
arch/um/kernel/time_kern.c 普通文件
查看文件

@@ -0,0 +1,203 @@
/*
* 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 "asm/irq.h"
#include "asm/param.h"
#include "asm/current.h"
#include "kern_util.h"
#include "user_util.h"
#include "time_user.h"
#include "mode.h"
#include "os.h"
u64 jiffies_64 = INITIAL_JIFFIES;
EXPORT_SYMBOL(jiffies_64);
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);
}
/* Changed at early boot */
int timer_irq_inited = 0;
static int first_tick;
static unsigned long long prev_usecs;
#ifdef CONFIG_UML_REAL_TIME_CLOCK
static long long delta; /* Deviation per interval */
#endif
#define MILLION 1000000
void timer_irq(union uml_pt_regs *regs)
{
unsigned long long ticks = 0;
if(!timer_irq_inited){
/* This is to ensure that ticks don't pile up when
* the timer handler is suspended */
first_tick = 0;
return;
}
if(first_tick){
#ifdef CONFIG_UML_REAL_TIME_CLOCK
/* We've had 1 tick */
unsigned long long usecs = os_usecs();
delta += usecs - prev_usecs;
prev_usecs = usecs;
/* Protect against the host clock being set backwards */
if(delta < 0)
delta = 0;
ticks += (delta * HZ) / MILLION;
delta -= (ticks * MILLION) / HZ;
#else
ticks = 1;
#endif
}
else {
prev_usecs = os_usecs();
first_tick = 1;
}
while(ticks > 0){
do_IRQ(TIMER_IRQ, regs);
ticks--;
}
}
void boot_timer_handler(int sig)
{
struct pt_regs regs;
CHOOSE_MODE((void)
(UPT_SC(&regs.regs) = (struct sigcontext *) (&sig + 1)),
(void) (regs.regs.skas.is_user = 0));
do_timer(&regs);
}
irqreturn_t um_timer(int irq, void *dev, struct pt_regs *regs)
{
unsigned long flags;
do_timer(regs);
write_seqlock_irqsave(&xtime_lock, flags);
timer();
write_sequnlock_irqrestore(&xtime_lock, flags);
return(IRQ_HANDLED);
}
long um_time(int __user *tloc)
{
struct timeval now;
do_gettimeofday(&now);
if (tloc) {
if (put_user(now.tv_sec, tloc))
now.tv_sec = -EFAULT;
}
return now.tv_sec;
}
long um_stime(int __user *tptr)
{
int value;
struct timespec new;
if (get_user(value, tptr))
return -EFAULT;
new.tv_sec = value;
new.tv_nsec = 0;
do_settimeofday(&new);
return 0;
}
void __udelay(unsigned long usecs)
{
int i, n;
n = (loops_per_jiffy * HZ * usecs) / MILLION;
for(i=0;i<n;i++) ;
}
void __const_udelay(unsigned long usecs)
{
int i, n;
n = (loops_per_jiffy * HZ * usecs) / MILLION;
for(i=0;i<n;i++) ;
}
void timer_handler(int sig, union uml_pt_regs *regs)
{
local_irq_disable();
update_process_times(CHOOSE_MODE(user_context(UPT_SP(regs)), (regs)->skas.is_user));
local_irq_enable();
if(current_thread->cpu == 0)
timer_irq(regs);
}
static DEFINE_SPINLOCK(timer_spinlock);
unsigned long time_lock(void)
{
unsigned long flags;
spin_lock_irqsave(&timer_spinlock, flags);
return(flags);
}
void time_unlock(unsigned long flags)
{
spin_unlock_irqrestore(&timer_spinlock, flags);
}
int __init timer_init(void)
{
int err;
CHOOSE_MODE(user_time_init_tt(), user_time_init_skas());
err = request_irq(TIMER_IRQ, um_timer, SA_INTERRUPT, "timer", NULL);
if(err != 0)
printk(KERN_ERR "timer_init : request_irq failed - "
"errno = %d\n", -err);
timer_irq_inited = 1;
return(0);
}
__initcall(timer_init);
/*
* 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:
*/

369
arch/um/kernel/tlb.c 普通文件
查看文件

@@ -0,0 +1,369 @@
/*
* 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"
#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 data,
void (*do_ops)(int, struct host_vm_op *, int))
{
pgd_t *npgd;
pud_t *npud;
pmd_t *npmd;
pte_t *npte;
unsigned long addr, end;
int r, w, x;
struct host_vm_op ops[16];
int op_index = -1, last_op = sizeof(ops) / sizeof(ops[0]) - 1;
if(mm == NULL) return;
for(addr = start_addr; addr < end_addr;){
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)){
op_index = add_munmap(addr, end - addr, ops,
op_index, last_op, data,
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)){
op_index = add_munmap(addr, end - addr, ops,
op_index, last_op, data,
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)){
op_index = add_munmap(addr, end - addr, ops,
op_index, last_op, data,
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_dirty(*npte))
w = 0;
if(!pte_young(*npte)){
r = 0;
w = 0;
}
if(force || pte_newpage(*npte)){
if(pte_present(*npte))
op_index = add_mmap(addr,
pte_val(*npte) & PAGE_MASK,
PAGE_SIZE, r, w, x, ops,
op_index, last_op, data,
do_ops);
else op_index = add_munmap(addr, PAGE_SIZE, ops,
op_index, last_op, data,
do_ops);
}
else if(pte_newprot(*npte))
op_index = add_mprotect(addr, PAGE_SIZE, r, w, x, ops,
op_index, last_op, data,
do_ops);
*npte = pte_mkuptodate(*npte);
addr += PAGE_SIZE;
}
(*do_ops)(data, ops, op_index);
}
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;
protect_memory(addr, PAGE_SIZE, 1, 1, 1, 1);
}
addr += PAGE_SIZE;
}
return(updated);
}
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());
}
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));
}
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, int data,
void (*do_ops)(int, struct host_vm_op *, int))
{
__u64 offset;
struct host_vm_op *last;
int fd;
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(index);
}
}
if(index == last_filled){
(*do_ops)(data, ops, last_filled);
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(index);
}
int add_munmap(unsigned long addr, unsigned long len, struct host_vm_op *ops,
int index, int last_filled, int data,
void (*do_ops)(int, struct host_vm_op *, int))
{
struct host_vm_op *last;
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(index);
}
}
if(index == last_filled){
(*do_ops)(data, ops, last_filled);
index = -1;
}
ops[++index] = ((struct host_vm_op) { .type = MUNMAP,
.u = { .munmap = {
.addr = addr,
.len = len } } });
return(index);
}
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, int data,
void (*do_ops)(int, struct host_vm_op *, int))
{
struct host_vm_op *last;
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(index);
}
}
if(index == last_filled){
(*do_ops)(data, ops, last_filled);
index = -1;
}
ops[++index] = ((struct host_vm_op) { .type = MPROTECT,
.u = { .mprotect = {
.addr = addr,
.len = len,
.r = r,
.w = w,
.x = x } } });
return(index);
}

251
arch/um/kernel/trap_kern.c 普通文件
查看文件

@@ -0,0 +1,251 @@
/*
* 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/config.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 "user_util.h"
#include "kern_util.h"
#include "kern.h"
#include "chan_kern.h"
#include "mconsole_kern.h"
#include "2_5compat.h"
#include "mem.h"
#include "mem_kern.h"
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;
unsigned long page;
int err = -EFAULT;
*code_out = SEGV_MAPERR;
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(!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;
page = address & PAGE_MASK;
pgd = pgd_offset(mm, page);
pud = pud_offset(pgd, page);
pmd = pmd_offset(pud, page);
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, page);
pud = pud_offset(pgd, page);
pmd = pmd_offset(pud, page);
pte = pte_offset_kernel(pmd, page);
} while(!pte_present(*pte));
err = 0;
*pte = pte_mkyoung(*pte);
if(pte_write(*pte)) *pte = pte_mkdirty(*pte);
flush_tlb_page(vma, page);
out:
up_read(&mm->mmap_sem);
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 (current->pid == 1) {
up_read(&mm->mmap_sem);
yield();
down_read(&mm->mmap_sem);
goto survive;
}
goto out;
}
LIST_HEAD(physmem_remappers);
void register_remapper(struct remapper *info)
{
list_add(&info->list, &physmem_remappers);
}
static int check_remapped_addr(unsigned long address, int is_write)
{
struct remapper *remapper;
struct list_head *ele;
__u64 offset;
int fd;
fd = phys_mapping(__pa(address), &offset);
if(fd == -1)
return(0);
list_for_each(ele, &physmem_remappers){
remapper = list_entry(ele, struct remapper, list);
if((*remapper->proc)(fd, address, is_write, offset))
return(1);
}
return(0);
}
unsigned long segv(unsigned long address, unsigned long ip, int is_write,
int is_user, void *sc)
{
struct siginfo si;
void *catcher;
int err;
if(!is_user && (address >= start_vm) && (address < end_vm)){
flush_tlb_kernel_vm();
return(0);
}
else if(check_remapped_addr(address & PAGE_MASK, is_write))
return(0);
else if(current->mm == NULL)
panic("Segfault with no mm");
err = handle_page_fault(address, ip, is_write, is_user, &si.si_code);
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(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 *)address;
force_sig_info(SIGBUS, &si, current);
}
else if(err == -ENOMEM){
printk("VM: killing process %s\n", current->comm);
do_exit(SIGKILL);
}
else {
si.si_signo = SIGSEGV;
si.si_addr = (void *) address;
current->thread.cr2 = address;
current->thread.err = is_write;
force_sig_info(SIGSEGV, &si, current);
}
return(0);
}
void bad_segv(unsigned long address, unsigned long ip, int is_write)
{
struct siginfo si;
si.si_signo = SIGSEGV;
si.si_code = SEGV_ACCERR;
si.si_addr = (void *) address;
current->thread.cr2 = address;
current->thread.err = is_write;
force_sig_info(SIGSEGV, &si, current);
}
void relay_signal(int sig, union uml_pt_regs *regs)
{
if(arch_handle_signal(sig, regs)) return;
if(!UPT_IS_USER(regs))
panic("Kernel mode signal %d", sig);
force_sig(sig, current);
}
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);
}
void winch(int sig, union uml_pt_regs *regs)
{
do_IRQ(WINCH_IRQ, regs);
}
void trap_init(void)
{
}
DEFINE_SPINLOCK(trap_lock);
static int trap_index = 0;
int next_trap_index(int limit)
{
int ret;
spin_lock(&trap_lock);
ret = trap_index;
if(++trap_index == limit)
trap_index = 0;
spin_unlock(&trap_lock);
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:
*/

120
arch/um/kernel/trap_user.c 普通文件
查看文件

@@ -0,0 +1,120 @@
/*
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <stdlib.h>
#include <errno.h>
#include <setjmp.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <asm/page.h>
#include <asm/unistd.h>
#include <asm/ptrace.h>
#include "init.h"
#include "sysdep/ptrace.h"
#include "sigcontext.h"
#include "sysdep/sigcontext.h"
#include "irq_user.h"
#include "signal_user.h"
#include "time_user.h"
#include "task.h"
#include "mode.h"
#include "choose-mode.h"
#include "kern_util.h"
#include "user_util.h"
#include "os.h"
void kill_child_dead(int pid)
{
kill(pid, SIGKILL);
kill(pid, SIGCONT);
do {
int n;
CATCH_EINTR(n = waitpid(pid, NULL, 0));
if (n > 0)
kill(pid, SIGCONT);
else
break;
} while(1);
}
/* Unlocked - don't care if this is a bit off */
int nsegfaults = 0;
struct {
unsigned long address;
int is_write;
int pid;
unsigned long sp;
int is_user;
} segfault_record[1024];
void segv_handler(int sig, union uml_pt_regs *regs)
{
int index, max;
if(UPT_IS_USER(regs) && !UPT_SEGV_IS_FIXABLE(regs)){
bad_segv(UPT_FAULT_ADDR(regs), UPT_IP(regs),
UPT_FAULT_WRITE(regs));
return;
}
max = sizeof(segfault_record)/sizeof(segfault_record[0]);
index = next_trap_index(max);
nsegfaults++;
segfault_record[index].address = UPT_FAULT_ADDR(regs);
segfault_record[index].pid = os_getpid();
segfault_record[index].is_write = UPT_FAULT_WRITE(regs);
segfault_record[index].sp = UPT_SP(regs);
segfault_record[index].is_user = UPT_IS_USER(regs);
segv(UPT_FAULT_ADDR(regs), UPT_IP(regs), UPT_FAULT_WRITE(regs),
UPT_IS_USER(regs), regs);
}
void usr2_handler(int sig, union uml_pt_regs *regs)
{
CHOOSE_MODE(syscall_handler_tt(sig, regs), (void) 0);
}
struct signal_info sig_info[] = {
[ SIGTRAP ] { .handler = relay_signal,
.is_irq = 0 },
[ SIGFPE ] { .handler = relay_signal,
.is_irq = 0 },
[ SIGILL ] { .handler = relay_signal,
.is_irq = 0 },
[ SIGWINCH ] { .handler = winch,
.is_irq = 1 },
[ SIGBUS ] { .handler = bus_handler,
.is_irq = 0 },
[ SIGSEGV] { .handler = segv_handler,
.is_irq = 0 },
[ SIGIO ] { .handler = sigio_handler,
.is_irq = 1 },
[ SIGVTALRM ] { .handler = timer_handler,
.is_irq = 1 },
[ SIGALRM ] { .handler = timer_handler,
.is_irq = 1 },
[ SIGUSR2 ] { .handler = usr2_handler,
.is_irq = 0 },
};
void do_longjmp(void *b, int val)
{
sigjmp_buf *buf = b;
siglongjmp(*buf, val);
}
/*
* 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:
*/

28
arch/um/kernel/tt/Makefile 普通文件
查看文件

@@ -0,0 +1,28 @@
#
# Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com)
# Licensed under the GPL
#
extra-y := unmap_fin.o
clean-files := unmap_tmp.o
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 time.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 time.o tracer.o
include arch/um/scripts/Makefile.rules
UNMAP_CFLAGS := $(patsubst -pg -DPROFILING,,$(USER_CFLAGS))
UNMAP_CFLAGS := $(patsubst -fprofile-arcs -ftest-coverage,,$(UNMAP_CFLAGS))
#XXX: partially copied from arch/um/scripts/Makefile.rules
$(obj)/unmap.o: c_flags = -Wp,-MD,$(depfile) $(UNMAP_CFLAGS)
$(obj)/unmap_fin.o : $(obj)/unmap.o
$(LD) -r -o $(obj)/unmap_tmp.o $< $(shell $(CC) -print-file-name=libc.a)
$(OBJCOPY) $(obj)/unmap_tmp.o $@ -G switcheroo

查看文件

@@ -0,0 +1,87 @@
/*
* 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 "time_user.h"
#include "signal_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(1);
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(current->thread_info, 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:
*/

查看文件

@@ -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:
*/

278
arch/um/kernel/tt/gdb.c 普通文件
查看文件

@@ -0,0 +1,278 @@
/*
* 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"
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)
{
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(char *unused)
{
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:
*/

查看文件

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/init.h"
#include "linux/config.h"
#include "mconsole_kern.h"
#ifdef CONFIG_MCONSOLE
extern int gdb_config(char *str);
extern int gdb_remove(char *unused);
static struct mc_device gdb_mc = {
.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:
*/

查看文件

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) and
* Lars Brinkhoff.
* Licensed under the GPL
*/
#ifndef __DEBUG_H
#define __DEBUG_H
extern int debugger_proxy(int status, pid_t pid);
extern void child_proxy(pid_t pid, int status);
extern void init_proxy (pid_t pid, int waiting, int status);
extern int start_debugger(char *prog, int startup, int stop, int *debugger_fd);
extern void fake_child_exit(void);
extern int gdb_config(char *str);
extern int gdb_remove(char *unused);
#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:
*/

查看文件

@@ -0,0 +1,23 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#ifndef __TT_MMU_H
#define __TT_MMU_H
struct mmu_context_tt {
};
#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:
*/

查看文件

@@ -0,0 +1,35 @@
/*
* 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 user_time_init_tt(void);
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:
*/

查看文件

@@ -0,0 +1,53 @@
/*
* 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 exit_thread_tt(void);
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:
*/

查看文件

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#ifndef __TT_H__
#define __TT_H__
#include "sysdep/ptrace.h"
extern int gdb_pid;
extern int debug;
extern int debug_stop;
extern int debug_trace;
extern int honeypot;
extern int fork_tramp(void *sig_stack);
extern int do_proc_op(void *t, int proc_id);
extern int tracer(int (*init_proc)(void *), void *sp);
extern void attach_process(int pid);
extern void tracer_panic(char *format, ...);
extern void set_init_pid(int pid);
extern int set_user_mode(void *task);
extern void set_tracing(void *t, int tracing);
extern int is_tracing(void *task);
extern void syscall_handler(int sig, union uml_pt_regs *regs);
extern void exit_kernel(int pid, void *task);
extern void do_syscall(void *task, int pid, int local_using_sysemu);
extern void do_sigtrap(void *task);
extern int is_valid_pid(int pid);
extern void remap_data(void *segment_start, void *segment_end, int w);
extern long execute_syscall_tt(void *r);
#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:
*/

查看文件

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
* Licensed under the GPL
*/
#ifndef __TT_UACCESS_H
#define __TT_UACCESS_H
#include "linux/string.h"
#include "linux/sched.h"
#include "asm/processor.h"
#include "asm/errno.h"
#include "asm/current.h"
#include "asm/a.out.h"
#include "uml_uaccess.h"
#define ABOVE_KMEM (16 * 1024 * 1024)
extern unsigned long end_vm;
extern unsigned long uml_physmem;
#define under_task_size(addr, size) \
(((unsigned long) (addr) < TASK_SIZE) && \
(((unsigned long) (addr) + (size)) < TASK_SIZE))
#define is_stack(addr, size) \
(((unsigned long) (addr) < STACK_TOP) && \
((unsigned long) (addr) >= STACK_TOP - ABOVE_KMEM) && \
(((unsigned long) (addr) + (size)) <= STACK_TOP))
#define access_ok_tt(type, addr, size) \
((type == VERIFY_READ) || (segment_eq(get_fs(), KERNEL_DS)) || \
(((unsigned long) (addr) <= ((unsigned long) (addr) + (size))) && \
(under_task_size(addr, size) || is_stack(addr, size))))
static inline int verify_area_tt(int type, const void * addr,
unsigned long size)
{
return(access_ok_tt(type, addr, size) ? 0 : -EFAULT);
}
extern unsigned long get_fault_addr(void);
extern int __do_copy_from_user(void *to, const void *from, int n,
void **fault_addr, void **fault_catcher);
extern int __do_strncpy_from_user(char *dst, const char *src, size_t n,
void **fault_addr, void **fault_catcher);
extern int __do_clear_user(void *mem, size_t len, void **fault_addr,
void **fault_catcher);
extern int __do_strnlen_user(const char *str, unsigned long n,
void **fault_addr, void **fault_catcher);
extern int copy_from_user_tt(void *to, const void *from, int n);
extern int copy_to_user_tt(void *to, const void *from, int n);
extern int strncpy_from_user_tt(char *dst, const char *src, int count);
extern int __clear_user_tt(void *mem, int len);
extern int clear_user_tt(void *mem, int len);
extern int strnlen_user_tt(const void *str, int len);
#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:
*/

28
arch/um/kernel/tt/ksyms.c 普通文件
查看文件

@@ -0,0 +1,28 @@
/*
* 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(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:
*/

51
arch/um/kernel/tt/mem.c 普通文件
查看文件

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/stddef.h"
#include "linux/config.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);
}
#ifdef CONFIG_HOST_2G_2G
#define TOP 0x80000000
#else
#define TOP 0xc0000000
#endif
#define SIZE ((CONFIG_NEST_LEVEL + CONFIG_KERNEL_HALF_GIGS) * 0x20000000)
#define START (TOP - SIZE)
unsigned long set_task_sizes_tt(int arg, unsigned long *host_size_out,
unsigned long *task_size_out)
{
/* Round up to the nearest 4M */
*host_size_out = ROUND_4M((unsigned long) &arg);
*task_size_out = START;
return(START);
}
/*
* 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:
*/

查看文件

@@ -0,0 +1,49 @@
/*
* 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"
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:
*/

查看文件

@@ -0,0 +1,476 @@
/*
* 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 "signal_user.h"
#include "kern_util.h"
#include "user_util.h"
#include "os.h"
#include "kern.h"
#include "sigcontext.h"
#include "time_user.h"
#include "mem_user.h"
#include "tlb.h"
#include "mode.h"
#include "init.h"
#include "tt.h"
void *switch_to_tt(void *prev, void *next, void *last)
{
struct task_struct *from, *to, *prev_sched;
unsigned long flags;
int err, vtalrm, alrm, prof, cpu;
char c;
/* jailing and SMP are incompatible, so this doesn't need to be
* made per-cpu
*/
static int reading;
from = prev;
to = next;
to->thread.prev_sched = from;
cpu = from->thread_info->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;
set_current(to);
reading = 0;
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);
reading = 1;
if((from->exit_state == EXIT_ZOMBIE) ||
(from->exit_state == EXIT_DEAD))
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->exit_state == EXIT_ZOMBIE) ||
(prev_sched->exit_state == EXIT_DEAD))
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();
flush_tlb_all();
local_irq_restore(flags);
return(current->thread.prev_sched);
}
void release_thread_tt(struct task_struct *task)
{
int pid = task->thread.mode.tt.extern_pid;
if(os_getpid() != pid)
os_kill_process(pid, 0);
}
void exit_thread_tt(void)
{
os_close_file(current->thread.mode.tt.switch_pipe[0]);
os_close_file(current->thread.mode.tt.switch_pipe[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(task_t *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(&current->thread.regs.regs) = (void *) (&sig + 1);
disable = (1 << (SIGVTALRM - 1)) | (1 << (SIGALRM - 1)) |
(1 << (SIGIO - 1)) | (1 << (SIGPROF - 1));
SC_SIGMASK(UPT_SC(&current->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(1);
enable_timer();
free_page(current->thread.temp_stack);
set_cmdline("(kernel thread)");
change_sig(SIGUSR1, 1);
change_sig(SIGVTALRM, 1);
change_sig(SIGPROF, 1);
local_irq_enable();
if(!run_kernel_thread(fn, arg, &current->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(&current->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(p->thread_info, 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(&current->thread.regs.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->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 = (void *) ((unsigned long) init_task.thread_info) +
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);
}
/*
* 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:
*/

查看文件

@@ -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

查看文件

@@ -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:
*/

查看文件

@@ -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:
*/

查看文件

@@ -0,0 +1,237 @@
/**********************************************************************
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"
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:
*/

查看文件

@@ -0,0 +1,70 @@
/**********************************************************************
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"
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:
*/

查看文件

@@ -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:
*/

查看文件

@@ -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:
*/

查看文件

@@ -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

查看文件

@@ -0,0 +1,47 @@
/*
* 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 "kern_util.h"
extern syscall_handler_t *sys_call_table[];
long execute_syscall_tt(void *r)
{
struct pt_regs *regs = r;
long res;
int syscall;
#ifdef CONFIG_SYSCALL_DEBUG
current->thread.nsyscalls++;
nsyscalls++;
#endif
syscall = UPT_SYSCALL_NR(&regs->regs);
if((syscall >= NR_syscalls) || (syscall < 0))
res = -ENOSYS;
else res = EXECUTE_SYSCALL(syscall, regs);
return(res);
}
/*
* 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:
*/

查看文件

@@ -0,0 +1,90 @@
/*
* 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_user.h"
#include "tt.h"
void syscall_handler_tt(int sig, union uml_pt_regs *regs)
{
void *sc;
long result;
int syscall;
#ifdef UML_CONFIG_DEBUG_SYSCALL
int index;
#endif
syscall = UPT_SYSCALL_NR(regs);
sc = UPT_SC(regs);
SC_START_SYSCALL(sc);
#ifdef UML_CONFIG_DEBUG_SYSCALL
index = record_syscall_start(syscall);
#endif
syscall_trace(regs, 0);
result = execute_syscall_tt(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(regs) = sc;
SC_SET_SYSCALL_RETURN(sc, result);
syscall_trace(regs, 1);
#ifdef UML_CONFIG_DEBUG_SYSCALL
record_syscall_end(index, result);
#endif
}
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);
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:
*/

28
arch/um/kernel/tt/time.c 普通文件
查看文件

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <signal.h>
#include <sys/time.h>
#include <time_user.h>
#include "process.h"
#include "user.h"
void user_time_init_tt(void)
{
if(signal(SIGVTALRM, (__sighandler_t) alarm_handler) == SIG_ERR)
panic("Couldn't set SIGVTALRM handler");
set_interval(ITIMER_VIRTUAL);
}
/*
* 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:
*/

149
arch/um/kernel/tt/tlb.c 普通文件
查看文件

@@ -0,0 +1,149 @@
/*
* 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 void do_ops(int unused, struct host_vm_op *ops, int last)
{
struct host_vm_op *op;
int i;
for(i = 0; i <= last; i++){
op = &ops[i];
switch(op->type){
case MMAP:
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:
os_unmap_memory((void *) op->u.munmap.addr,
op->u.munmap.len);
break;
case MPROTECT:
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;
}
}
}
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, 0, 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);
}
static void protect_vm_page(unsigned long addr, int w, int must_succeed)
{
int err;
err = protect_memory(addr, PAGE_SIZE, 1, w, 1, must_succeed);
if(err == 0) return;
else if((err == -EFAULT) || (err == -ENOMEM)){
flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
protect_vm_page(addr, w, 1);
}
else panic("protect_vm_page : protect failed, errno = %d\n", err);
}
void mprotect_kernel_vm(int w)
{
struct mm_struct *mm;
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
unsigned long addr;
mm = &init_mm;
for(addr = start_vm; addr < end_vm;){
pgd = pgd_offset(mm, addr);
pud = pud_offset(pgd, addr);
pmd = pmd_offset(pud, addr);
if(pmd_present(*pmd)){
pte = pte_offset_kernel(pmd, addr);
if(pte_present(*pte)) protect_vm_page(addr, w, 0);
addr += PAGE_SIZE;
}
else addr += PMD_SIZE;
}
}
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);
}

480
arch/um/kernel/tt/tracer.c 普通文件
查看文件

@@ -0,0 +1,480 @@
/*
* 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 "signal_user.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 "mode.h"
#include "tt.h"
static int tracer_winch[2];
int is_tracer_winch(int pid, int fd, void *data)
{
if(pid != tracing_pid)
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)
{
printf("Tracing thread segfault at address 0x%lx, ip 0x%lx\n",
SC_FAULT_ADDR(&sc), 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());
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 int io_nsignals, io_count, intr_count;
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;
#ifdef UML_CONFIG_SYSCALL_DEBUG
unsigned long eip = 0;
int last_index;
#endif
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);
#ifdef UML_CONFIG_SYSCALL_DEBUG
if(signal_index[proc_id] == 1024){
signal_index[proc_id] = 0;
last_index = 1023;
}
else last_index = signal_index[proc_id] - 1;
if(((sig == SIGPROF) || (sig == SIGVTALRM) ||
(sig == SIGALRM)) &&
(signal_record[proc_id][last_index].signal == sig)&&
(signal_record[proc_id][last_index].pid == pid))
signal_index[proc_id] = last_index;
signal_record[proc_id][signal_index[proc_id]].pid = pid;
gettimeofday(&signal_record[proc_id][signal_index[proc_id]].time, NULL);
eip = ptrace(PTRACE_PEEKUSR, pid, PT_IP_OFFSET, 0);
signal_record[proc_id][signal_index[proc_id]].addr = eip;
signal_record[proc_id][signal_index[proc_id]++].signal = sig;
#endif
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:
*/

查看文件

@@ -0,0 +1,60 @@
/*
* 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 "signal_user.h"
#include "user_util.h"
#include "kern_util.h"
#include "task.h"
#include "tt.h"
void sig_handler_common_tt(int sig, void *sc_ptr)
{
struct sigcontext *sc = sc_ptr;
struct tt_regs save_regs, *r;
struct signal_info *info;
int save_errno = errno, is_user;
/* 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;
save_regs = *r;
is_user = user_context(SC_SP(sc));
r->sc = sc;
if(sig != SIGUSR2)
r->syscall = -1;
info = &sig_info[sig];
if(!info->is_irq) unblock_signals();
(*info->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 普通文件
查看文件

@@ -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_tt(VERIFY_READ, from, n))
return(n);
return(__do_copy_from_user(to, from, n, &current->thread.fault_addr,
&current->thread.fault_catcher));
}
int copy_to_user_tt(void __user *to, const void *from, int n)
{
if(!access_ok_tt(VERIFY_WRITE, to, n))
return(n);
return(__do_copy_to_user(to, from, n, &current->thread.fault_addr,
&current->thread.fault_catcher));
}
int strncpy_from_user_tt(char *dst, const char __user *src, int count)
{
int n;
if(!access_ok_tt(VERIFY_READ, src, 1))
return(-EFAULT);
n = __do_strncpy_from_user(dst, src, count,
&current->thread.fault_addr,
&current->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,
&current->thread.fault_addr,
&current->thread.fault_catcher));
}
int clear_user_tt(void __user *mem, int len)
{
if(!access_ok_tt(VERIFY_WRITE, mem, len))
return(len);
return(__do_clear_user(mem, len, &current->thread.fault_addr,
&current->thread.fault_catcher));
}
int strnlen_user_tt(const void __user *str, int len)
{
return(__do_strnlen_user(str, len,
&current->thread.fault_addr,
&current->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:
*/

查看文件

@@ -0,0 +1,98 @@
/*
* Copyright (C) 2001 Chris Emerson (cemerson@chiark.greenend.org.uk)
* Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <setjmp.h>
#include <string.h>
#include "user_util.h"
#include "uml_uaccess.h"
#include "task.h"
#include "kern_util.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 return(n - (fault - (unsigned long) from));
}
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;
sigjmp_buf jbuf;
*fault_catcher = &jbuf;
if(sigsetjmp(jbuf, 1) == 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:
*/

31
arch/um/kernel/tt/unmap.c 普通文件
查看文件

@@ -0,0 +1,31 @@
/*
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <sys/mman.h>
int switcheroo(int fd, int prot, void *from, void *to, int size)
{
if(munmap(to, size) < 0){
return(-1);
}
if(mmap(to, size, prot, MAP_SHARED | MAP_FIXED, fd, 0) != to){
return(-1);
}
if(munmap(from, size) < 0){
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:
*/

230
arch/um/kernel/tty_log.c 普通文件
查看文件

@@ -0,0 +1,230 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com) and
* geoffrey hing <ghing@net.ohio-state.edu>
* Licensed under the GPL
*/
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include "init.h"
#include "user.h"
#include "kern_util.h"
#include "os.h"
#define TTY_LOG_DIR "./"
/* Set early in boot and then unchanged */
static char *tty_log_dir = TTY_LOG_DIR;
static int tty_log_fd = -1;
#define TTY_LOG_OPEN 1
#define TTY_LOG_CLOSE 2
#define TTY_LOG_WRITE 3
#define TTY_LOG_EXEC 4
#define TTY_READ 1
#define TTY_WRITE 2
struct tty_log_buf {
int what;
unsigned long tty;
int len;
int direction;
unsigned long sec;
unsigned long usec;
};
int open_tty_log(void *tty, void *current_tty)
{
struct timeval tv;
struct tty_log_buf data;
char buf[strlen(tty_log_dir) + sizeof("01234567890-01234567\0")];
int fd;
gettimeofday(&tv, NULL);
if(tty_log_fd != -1){
data = ((struct tty_log_buf) { .what = TTY_LOG_OPEN,
.tty = (unsigned long) tty,
.len = sizeof(current_tty),
.direction = 0,
.sec = tv.tv_sec,
.usec = tv.tv_usec } );
os_write_file(tty_log_fd, &data, sizeof(data));
os_write_file(tty_log_fd, &current_tty, data.len);
return(tty_log_fd);
}
sprintf(buf, "%s/%0u-%0u", tty_log_dir, (unsigned int) tv.tv_sec,
(unsigned int) tv.tv_usec);
fd = os_open_file(buf, of_append(of_create(of_rdwr(OPENFLAGS()))),
0644);
if(fd < 0){
printk("open_tty_log : couldn't open '%s', errno = %d\n",
buf, -fd);
}
return(fd);
}
void close_tty_log(int fd, void *tty)
{
struct tty_log_buf data;
struct timeval tv;
if(tty_log_fd != -1){
gettimeofday(&tv, NULL);
data = ((struct tty_log_buf) { .what = TTY_LOG_CLOSE,
.tty = (unsigned long) tty,
.len = 0,
.direction = 0,
.sec = tv.tv_sec,
.usec = tv.tv_usec } );
os_write_file(tty_log_fd, &data, sizeof(data));
return;
}
os_close_file(fd);
}
static int log_chunk(int fd, const char *buf, int len)
{
int total = 0, try, missed, n;
char chunk[64];
while(len > 0){
try = (len > sizeof(chunk)) ? sizeof(chunk) : len;
missed = copy_from_user_proc(chunk, (char *) buf, try);
try -= missed;
n = os_write_file(fd, chunk, try);
if(n != try) {
if(n < 0)
return(n);
return(-EIO);
}
if(missed != 0)
return(-EFAULT);
len -= try;
total += try;
buf += try;
}
return(total);
}
int write_tty_log(int fd, const char *buf, int len, void *tty, int is_read)
{
struct timeval tv;
struct tty_log_buf data;
int direction;
if(fd == tty_log_fd){
gettimeofday(&tv, NULL);
direction = is_read ? TTY_READ : TTY_WRITE;
data = ((struct tty_log_buf) { .what = TTY_LOG_WRITE,
.tty = (unsigned long) tty,
.len = len,
.direction = direction,
.sec = tv.tv_sec,
.usec = tv.tv_usec } );
os_write_file(tty_log_fd, &data, sizeof(data));
}
return(log_chunk(fd, buf, len));
}
void log_exec(char **argv, void *tty)
{
struct timeval tv;
struct tty_log_buf data;
char **ptr,*arg;
int len;
if(tty_log_fd == -1) return;
gettimeofday(&tv, NULL);
len = 0;
for(ptr = argv; ; ptr++){
if(copy_from_user_proc(&arg, ptr, sizeof(arg)))
return;
if(arg == NULL) break;
len += strlen_user_proc(arg);
}
data = ((struct tty_log_buf) { .what = TTY_LOG_EXEC,
.tty = (unsigned long) tty,
.len = len,
.direction = 0,
.sec = tv.tv_sec,
.usec = tv.tv_usec } );
os_write_file(tty_log_fd, &data, sizeof(data));
for(ptr = argv; ; ptr++){
if(copy_from_user_proc(&arg, ptr, sizeof(arg)))
return;
if(arg == NULL) break;
log_chunk(tty_log_fd, arg, strlen_user_proc(arg));
}
}
extern void register_tty_logger(int (*opener)(void *, void *),
int (*writer)(int, const char *, int,
void *, int),
void (*closer)(int, void *));
static int register_logger(void)
{
register_tty_logger(open_tty_log, write_tty_log, close_tty_log);
return(0);
}
__uml_initcall(register_logger);
static int __init set_tty_log_dir(char *name, int *add)
{
tty_log_dir = name;
return 0;
}
__uml_setup("tty_log_dir=", set_tty_log_dir,
"tty_log_dir=<directory>\n"
" This is used to specify the directory where the logs of all pty\n"
" data from this UML machine will be written.\n\n"
);
static int __init set_tty_log_fd(char *name, int *add)
{
char *end;
tty_log_fd = strtoul(name, &end, 0);
if((*end != '\0') || (end == name)){
printf("set_tty_log_fd - strtoul failed on '%s'\n", name);
tty_log_fd = -1;
}
*add = 0;
return 0;
}
__uml_setup("tty_log_fd=", set_tty_log_fd,
"tty_log_fd=<fd>\n"
" This is used to specify a preconfigured file descriptor to which all\n"
" tty data will be written. Preconfigure the descriptor with something\n"
" like '10>tty_log tty_log_fd=10'.\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:
*/

查看文件

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2001 Chris Emerson (cemerson@chiark.greenend.org.uk)
* Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <setjmp.h>
#include <string.h>
/* 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.
*/
unsigned long __do_user_copy(void *to, const void *from, int n,
void **fault_addr, void **fault_catcher,
void (*op)(void *to, const void *from,
int n), int *faulted_out)
{
unsigned long *faddrp = (unsigned long *) fault_addr, ret;
sigjmp_buf jbuf;
*fault_catcher = &jbuf;
if(sigsetjmp(jbuf, 1) == 0){
(*op)(to, from, n);
ret = 0;
*faulted_out = 0;
}
else {
ret = *faddrp;
*faulted_out = 1;
}
*fault_addr = NULL;
*fault_catcher = NULL;
return ret;
}
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));
}
/*
* 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:
*/

467
arch/um/kernel/um_arch.c 普通文件
查看文件

@@ -0,0 +1,467 @@
/*
* Copyright (C) 2000, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/config.h"
#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 "ubd_user.h"
#include "asm/current.h"
#include "asm/setup.h"
#include "user_util.h"
#include "kern_util.h"
#include "kern.h"
#include "mem_user.h"
#include "mem.h"
#include "umid.h"
#include "initrd.h"
#include "init.h"
#include "os.h"
#include "choose-mode.h"
#include "mode_kern.h"
#include "mode.h"
#define DEFAULT_COMMAND_LINE "root=98:0"
/* Changed in linux_main and setup_arch, which run before SMP is started */
char command_line[COMMAND_LINE_SIZE] = { 0 };
void 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)
{
}
struct seq_operations cpuinfo_op = {
.start = c_start,
.next = c_next,
.stop = c_stop,
.show = show_cpuinfo,
};
pte_t * __bad_pagetable(void)
{
panic("Someone should implement __bad_pagetable");
return(NULL);
}
/* 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_MODE_TT
/* 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 physmem_size = 32 * 1024 * 1024;
void set_cmdline(char *cmd)
{
#ifdef CONFIG_MODE_TT
char *umid, *ptr;
if(CHOOSE_MODE(honeypot, 0)) return;
umid = get_umid(1);
if(umid != NULL){
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", system_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"
);
#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);
}
#else
#error Either CONFIG_MODE_TT or CONFIG_MODE_SKAS must be enabled
#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, system_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)
int linux_main(int argc, char **argv)
{
unsigned long avail, diff;
unsigned long virtmem_size, max_physmem;
unsigned int i, add;
for (i = 1; i < argc; i++){
if((i == 1) && (argv[i][0] == ' ')) continue;
add = 1;
uml_checksetup(argv[i], &add);
if (add)
add_arg(argv[i]);
}
if(have_root == 0)
add_arg(DEFAULT_COMMAND_LINE);
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
uml_start = CHOOSE_MODE_PROC(set_task_sizes_tt, set_task_sizes_skas, 0,
&host_task_size, &task_size);
/* Need to check this early because mmapping happens before the
* kernel is running.
*/
check_tmpexec();
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;
/* Reserve up to 4M after the current brk */
uml_reserved = ROUND_4M(brk_start) + (1 << 22);
setup_machinename(system_utsname.machine);
#ifdef CONFIG_MODE_TT
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 %ld 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 %ld bytes of physical "
"memory and %ld 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 %ld 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)
{
notifier_chain_register(&panic_notifier_list, &panic_exit_notifier);
paging_init();
strlcpy(saved_command_line, command_line, COMMAND_LINE_SIZE);
*cmdline_p = command_line;
setup_hostinfo();
}
void __init check_bugs(void)
{
arch_check_bugs();
check_ptrace();
check_sigio();
check_devanon();
}
void apply_alternatives(void *start, void *end)
{
}

325
arch/um/kernel/umid.c 普通文件
查看文件

@@ -0,0 +1,325 @@
/*
* Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/param.h>
#include "user.h"
#include "umid.h"
#include "init.h"
#include "os.h"
#include "user_util.h"
#include "choose-mode.h"
#define UMID_LEN 64
#define UML_DIR "~/.uml/"
/* Changed by set_umid and make_umid, which are run early in boot */
static char umid[UMID_LEN] = { 0 };
/* Changed by set_uml_dir and make_uml_dir, which are run early in boot */
static char *uml_dir = UML_DIR;
/* Changed by set_umid */
static int umid_is_random = 1;
static int umid_inited = 0;
static int make_umid(int (*printer)(const char *fmt, ...));
static int __init set_umid(char *name, int is_random,
int (*printer)(const char *fmt, ...))
{
if(umid_inited){
(*printer)("Unique machine name can't be set twice\n");
return(-1);
}
if(strlen(name) > UMID_LEN - 1)
(*printer)("Unique machine name is being truncated to %d "
"characters\n", UMID_LEN);
strlcpy(umid, name, sizeof(umid));
umid_is_random = is_random;
umid_inited = 1;
return 0;
}
static int __init set_umid_arg(char *name, int *add)
{
*add = 0;
return(set_umid(name, 0, printf));
}
__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"
);
int __init umid_file_name(char *name, char *buf, int len)
{
int n;
if(!umid_inited && make_umid(printk)) return(-1);
n = strlen(uml_dir) + strlen(umid) + strlen(name) + 1;
if(n > len){
printk("umid_file_name : buffer too short\n");
return(-1);
}
sprintf(buf, "%s%s/%s", uml_dir, umid, name);
return(0);
}
extern int tracing_pid;
static int __init create_pid_file(void)
{
char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")];
char pid[sizeof("nnnnn\0")];
int fd, n;
if(umid_file_name("pid", file, sizeof(file))) return 0;
fd = os_open_file(file, of_create(of_excl(of_rdwr(OPENFLAGS()))),
0644);
if(fd < 0){
printf("Open of machine pid file \"%s\" failed: %s\n",
file, strerror(-fd));
return 0;
}
sprintf(pid, "%d\n", os_getpid());
n = os_write_file(fd, pid, strlen(pid));
if(n != strlen(pid))
printf("Write of pid file failed - err = %d\n", -n);
os_close_file(fd);
return 0;
}
static int actually_do_remove(char *dir)
{
DIR *directory;
struct dirent *ent;
int len;
char file[256];
directory = opendir(dir);
if(directory == NULL){
printk("actually_do_remove : couldn't open directory '%s', "
"errno = %d\n", dir, errno);
return(1);
}
while((ent = readdir(directory)) != NULL){
if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
continue;
len = strlen(dir) + sizeof("/") + strlen(ent->d_name) + 1;
if(len > sizeof(file)){
printk("Not deleting '%s' from '%s' - name too long\n",
ent->d_name, dir);
continue;
}
sprintf(file, "%s/%s", dir, ent->d_name);
if(unlink(file) < 0){
printk("actually_do_remove : couldn't remove '%s' "
"from '%s', errno = %d\n", ent->d_name, dir,
errno);
return(1);
}
}
if(rmdir(dir) < 0){
printk("actually_do_remove : couldn't rmdir '%s', "
"errno = %d\n", dir, errno);
return(1);
}
return(0);
}
void remove_umid_dir(void)
{
char dir[strlen(uml_dir) + UMID_LEN + 1];
if(!umid_inited) return;
sprintf(dir, "%s%s", uml_dir, umid);
actually_do_remove(dir);
}
char *get_umid(int only_if_set)
{
if(only_if_set && umid_is_random) return(NULL);
return(umid);
}
int not_dead_yet(char *dir)
{
char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")];
char pid[sizeof("nnnnn\0")], *end;
int dead, fd, p, n;
sprintf(file, "%s/pid", dir);
dead = 0;
fd = os_open_file(file, of_read(OPENFLAGS()), 0);
if(fd < 0){
if(fd != -ENOENT){
printk("not_dead_yet : couldn't open pid file '%s', "
"err = %d\n", file, -fd);
return(1);
}
dead = 1;
}
if(fd > 0){
n = os_read_file(fd, pid, sizeof(pid));
if(n < 0){
printk("not_dead_yet : couldn't read pid file '%s', "
"err = %d\n", file, -n);
return(1);
}
p = strtoul(pid, &end, 0);
if(end == pid){
printk("not_dead_yet : couldn't parse pid file '%s', "
"errno = %d\n", file, errno);
dead = 1;
}
if(((kill(p, 0) < 0) && (errno == ESRCH)) ||
(p == CHOOSE_MODE(tracing_pid, os_getpid())))
dead = 1;
}
if(!dead) return(1);
return(actually_do_remove(dir));
}
static int __init set_uml_dir(char *name, int *add)
{
if((strlen(name) > 0) && (name[strlen(name) - 1] != '/')){
uml_dir = malloc(strlen(name) + 2);
if(uml_dir == NULL){
printf("Failed to malloc uml_dir - error = %d\n",
errno);
uml_dir = name;
/* Return 0 here because do_initcalls doesn't look at
* the return value.
*/
return(0);
}
sprintf(uml_dir, "%s/", name);
}
else uml_dir = name;
return(0);
}
static int __init make_uml_dir(void)
{
char dir[MAXPATHLEN + 1] = { '\0' };
int len;
if(*uml_dir == '~'){
char *home = getenv("HOME");
if(home == NULL){
printf("make_uml_dir : no value in environment for "
"$HOME\n");
exit(1);
}
strlcpy(dir, home, sizeof(dir));
uml_dir++;
}
len = strlen(dir);
strncat(dir, uml_dir, sizeof(dir) - len);
len = strlen(dir);
if((len > 0) && (len < sizeof(dir) - 1) && (dir[len - 1] != '/')){
dir[len] = '/';
dir[len + 1] = '\0';
}
uml_dir = malloc(strlen(dir) + 1);
if(uml_dir == NULL){
printf("make_uml_dir : malloc failed, errno = %d\n", errno);
exit(1);
}
strcpy(uml_dir, dir);
if((mkdir(uml_dir, 0777) < 0) && (errno != EEXIST)){
printf("Failed to mkdir %s: %s\n", uml_dir, strerror(errno));
return(-1);
}
return 0;
}
static int __init make_umid(int (*printer)(const char *fmt, ...))
{
int fd, err;
char tmp[strlen(uml_dir) + UMID_LEN + 1];
strlcpy(tmp, uml_dir, sizeof(tmp));
if(!umid_inited){
strcat(tmp, "XXXXXX");
fd = mkstemp(tmp);
if(fd < 0){
(*printer)("make_umid - mkstemp(%s) failed: %s\n",
tmp,strerror(errno));
return(1);
}
os_close_file(fd);
/* There's a nice tiny little race between this unlink and
* the mkdir below. It'd be nice if there were a mkstemp
* for directories.
*/
unlink(tmp);
set_umid(&tmp[strlen(uml_dir)], 1, printer);
}
sprintf(tmp, "%s%s", uml_dir, umid);
err = mkdir(tmp, 0777);
if(err < 0){
if(errno == EEXIST){
if(not_dead_yet(tmp)){
(*printer)("umid '%s' is in use\n", umid);
return(-1);
}
err = mkdir(tmp, 0777);
}
}
if(err < 0){
(*printer)("Failed to create %s - errno = %d\n", umid, errno);
return(-1);
}
return(0);
}
__uml_setup("uml_dir=", set_uml_dir,
"uml_dir=<directory>\n"
" The location to place the pid and umid files.\n\n"
);
static int __init make_umid_setup(void)
{
/* one function with the ordering we need ... */
make_uml_dir();
make_umid(printf);
return create_pid_file();
}
__uml_postsetup(make_umid_setup);
/*
* 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/uml.lds.S 普通文件
查看文件

@@ -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);
. = START + SIZEOF_HEADERS;
/* Used in arch/um/kernel/mem.c. Any memory between START and __binary_start
* is remapped.*/
__binary_start = .;
#ifdef MODE_TT
.thread_private : {
__start_thread_private = .;
errno = .;
. += 4;
arch/um/kernel/tt/unmap_fin.o (.data)
__end_thread_private = .;
}
. = ALIGN(4096);
.remap : { arch/um/kernel/tt/unmap_fin.o (.text) }
/* We want it only if we are in MODE_TT. In both cases, however, when MODE_TT
* is off the resulting binary segfaults.*/
. = ALIGN(4096); /* Init code and data */
#endif
_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*)
}
#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) }
/* 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 debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
}

173
arch/um/kernel/user_util.c 普通文件
查看文件

@@ -0,0 +1,173 @@
/*
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <setjmp.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <sys/param.h>
#include <sys/time.h>
#include "asm/types.h"
#include <ctype.h>
#include <signal.h>
#include <wait.h>
#include <errno.h>
#include <stdarg.h>
#include <sched.h>
#include <termios.h>
#include <string.h>
#include "user_util.h"
#include "kern_util.h"
#include "user.h"
#include "mem_user.h"
#include "init.h"
#include "helper.h"
#include "ptrace_user.h"
#include "uml-config.h"
void stop(void)
{
while(1) sleep(1000000);
}
void stack_protections(unsigned long address)
{
int prot = PROT_READ | PROT_WRITE | PROT_EXEC;
if(mprotect((void *) address, page_size(), prot) < 0)
panic("protecting stack failed, errno = %d", errno);
}
void task_protections(unsigned long address)
{
unsigned long guard = address + page_size();
unsigned long stack = guard + page_size();
int prot = 0, pages;
#ifdef notdef
if(mprotect((void *) stack, page_size(), prot) < 0)
panic("protecting guard page failed, errno = %d", errno);
#endif
pages = (1 << UML_CONFIG_KERNEL_STACK_ORDER) - 2;
prot = PROT_READ | PROT_WRITE | PROT_EXEC;
if(mprotect((void *) stack, pages * page_size(), prot) < 0)
panic("protecting stack failed, errno = %d", errno);
}
int wait_for_stop(int pid, int sig, int cont_type, void *relay)
{
sigset_t *relay_signals = relay;
int status, ret;
while(1){
CATCH_EINTR(ret = waitpid(pid, &status, WUNTRACED));
if((ret < 0) ||
!WIFSTOPPED(status) || (WSTOPSIG(status) != sig)){
if(ret < 0){
printk("wait failed, errno = %d\n",
errno);
}
else if(WIFEXITED(status))
printk("process %d exited with status %d\n",
pid, WEXITSTATUS(status));
else if(WIFSIGNALED(status))
printk("process %d exited with signal %d\n",
pid, WTERMSIG(status));
else if((WSTOPSIG(status) == SIGVTALRM) ||
(WSTOPSIG(status) == SIGALRM) ||
(WSTOPSIG(status) == SIGIO) ||
(WSTOPSIG(status) == SIGPROF) ||
(WSTOPSIG(status) == SIGCHLD) ||
(WSTOPSIG(status) == SIGWINCH) ||
(WSTOPSIG(status) == SIGINT)){
ptrace(cont_type, pid, 0, WSTOPSIG(status));
continue;
}
else if((relay_signals != NULL) &&
sigismember(relay_signals, WSTOPSIG(status))){
ptrace(cont_type, pid, 0, WSTOPSIG(status));
continue;
}
else printk("process %d stopped with signal %d\n",
pid, WSTOPSIG(status));
panic("wait_for_stop failed to wait for %d to stop "
"with %d\n", pid, sig);
}
return(status);
}
}
int raw(int fd)
{
struct termios tt;
int err;
CATCH_EINTR(err = tcgetattr(fd, &tt));
if (err < 0) {
printk("tcgetattr failed, errno = %d\n", errno);
return(-errno);
}
cfmakeraw(&tt);
CATCH_EINTR(err = tcsetattr(fd, TCSADRAIN, &tt));
if (err < 0) {
printk("tcsetattr failed, errno = %d\n", errno);
return(-errno);
}
/* XXX tcsetattr could have applied only some changes
* (and cfmakeraw() is a set of changes) */
return(0);
}
void setup_machinename(char *machine_out)
{
struct utsname host;
uname(&host);
strcpy(machine_out, host.machine);
}
char host_info[(_UTSNAME_LENGTH + 1) * 4 + _UTSNAME_NODENAME_LENGTH + 1];
void setup_hostinfo(void)
{
struct utsname host;
uname(&host);
sprintf(host_info, "%s %s %s %s %s", host.sysname, host.nodename,
host.release, host.version, host.machine);
}
int setjmp_wrapper(void (*proc)(void *, void *), ...)
{
va_list args;
sigjmp_buf buf;
int n;
n = sigsetjmp(buf, 1);
if(n == 0){
va_start(args, proc);
(*proc)(&buf, &args);
}
va_end(args);
return(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:
*/