Creation of Cybook 2416 (actually Gen4) repository

This commit is contained in:
mlt
2009-12-18 17:10:00 +00:00
committed by godzil
commit 76f20f4d40
13791 changed files with 6812321 additions and 0 deletions

47
arch/um/kernel/Makefile Normal file
View File

@@ -0,0 +1,47 @@
#
# Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
# Licensed under the GPL
#
extra-y := vmlinux.lds
clean-files :=
obj-y = config.o exec.o exitcode.o init_task.o irq.o ksyms.o mem.o \
physmem.o process.o ptrace.o reboot.o sigio.o \
signal.o smp.o syscall.o sysrq.o time.o tlb.o trap.o uaccess.o \
um_arch.o umid.o
obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o
obj-$(CONFIG_GPROF) += gprof_syms.o
obj-$(CONFIG_GCOV) += gmon_syms.o
obj-$(CONFIG_MODE_TT) += tt/
obj-$(CONFIG_MODE_SKAS) += skas/
USER_OBJS := config.o
include arch/um/scripts/Makefile.rules
targets := config.c config.tmp
# Be careful with the below Sed code - sed is pitfall-rich!
# We use sed to lower build requirements, for "embedded" builders for instance.
$(obj)/config.tmp: $(objtree)/.config FORCE
$(call if_changed,quote1)
quiet_cmd_quote1 = QUOTE $@
cmd_quote1 = sed -e 's/"/\\"/g' -e 's/^/"/' -e 's/$$/\\n"/' \
$< > $@
$(obj)/config.c: $(src)/config.c.in $(obj)/config.tmp FORCE
$(call if_changed,quote2)
quiet_cmd_quote2 = QUOTE $@
cmd_quote2 = sed -e '/CONFIG/{' \
-e 's/"CONFIG"\;/""/' \
-e 'r $(obj)/config.tmp' \
-e 'a \' \
-e '""\;' \
-e '}' \
$< > $@

View File

@@ -0,0 +1 @@
#include "sysdep/kernel-offsets.h"

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <stdio.h>
#include <stdlib.h>
#include "init.h"
static __initdata char *config = "CONFIG";
static int __init print_config(char *line, int *add)
{
printf("%s", config);
exit(0);
}
__uml_setup("--showconfig", print_config,
"--showconfig\n"
" Prints the config file that this UML binary was generated from.\n\n"
);
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

155
arch/um/kernel/dyn.lds.S Normal file
View File

@@ -0,0 +1,155 @@
#include <asm-generic/vmlinux.lds.h>
OUTPUT_FORMAT(ELF_FORMAT)
OUTPUT_ARCH(ELF_ARCH)
ENTRY(_start)
jiffies = jiffies_64;
SECTIONS
{
PROVIDE (__executable_start = START);
. = START + SIZEOF_HEADERS;
.interp : { *(.interp) }
/* Used in arch/um/kernel/mem.c. Any memory between START and __binary_start
* is remapped.*/
__binary_start = .;
. = ALIGN(4096); /* Init code and data */
_text = .;
_stext = .;
__init_begin = .;
.init.text : {
_sinittext = .;
*(.init.text)
_einittext = .;
}
. = ALIGN(4096);
/* Read-only sections, merged into text segment: */
.hash : { *(.hash) }
.gnu.hash : { *(.gnu.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rel.init : { *(.rel.init) }
.rela.init : { *(.rela.init) }
.rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) }
.rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) }
.rel.fini : { *(.rel.fini) }
.rela.fini : { *(.rela.fini) }
.rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) }
.rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) }
.rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) }
.rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) }
.rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) }
.rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
.rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) }
.rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
.rel.ctors : { *(.rel.ctors) }
.rela.ctors : { *(.rela.ctors) }
.rel.dtors : { *(.rel.dtors) }
.rela.dtors : { *(.rela.dtors) }
.rel.got : { *(.rel.got) }
.rela.got : { *(.rela.got) }
.rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) }
.rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) }
.rel.plt : { *(.rel.plt) }
.rela.plt : { *(.rela.plt) }
.init : {
KEEP (*(.init))
} =0x90909090
.plt : { *(.plt) }
.text : {
*(.text)
SCHED_TEXT
LOCK_TEXT
*(.fixup)
*(.stub .text.* .gnu.linkonce.t.*)
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
. = ALIGN(4096);
__syscall_stub_start = .;
*(.__syscall_stub*)
__syscall_stub_end = .;
. = ALIGN(4096);
} =0x90909090
.fini : {
KEEP (*(.fini))
} =0x90909090
.kstrtab : { *(.kstrtab) }
#include "asm/common.lds.S"
init.data : { *(.init.data) }
/* Ensure the __preinit_array_start label is properly aligned. We
could instead move the label definition inside the section, but
the linker would then create the section even if it turns out to
be empty, which isn't pretty. */
. = ALIGN(32 / 8);
.preinit_array : { *(.preinit_array) }
.init_array : { *(.init_array) }
.fini_array : { *(.fini_array) }
.data : {
. = ALIGN(KERNEL_STACK_SIZE); /* init_task */
*(.data.init_task)
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
.data1 : { *(.data1) }
.tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
.eh_frame : { KEEP (*(.eh_frame)) }
.gcc_except_table : { *(.gcc_except_table) }
.dynamic : { *(.dynamic) }
.ctors : {
/* gcc uses crtbegin.o to find the start of
the constructors, so we make sure it is
first. Because this is a wildcard, it
doesn't matter if the user does not
actually link against crtbegin.o; the
linker won't look for a file to match a
wildcard. The wildcard also means that it
doesn't matter which directory crtbegin.o
is in. */
KEEP (*crtbegin.o(.ctors))
/* We don't want to include the .ctor section from
from the crtend.o file until after the sorted ctors.
The .ctor section from the crtend file contains the
end of ctors marker and it must be last */
KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
}
.dtors : {
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
}
.jcr : { KEEP (*(.jcr)) }
.got : { *(.got.plt) *(.got) }
_edata = .;
PROVIDE (edata = .);
__bss_start = .;
.bss : {
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections. */
. = ALIGN(32 / 8);
. = ALIGN(32 / 8);
}
_end = .;
PROVIDE (end = .);
STABS_DEBUG
DWARF_DEBUG
}

89
arch/um/kernel/exec.c Normal file
View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/slab.h"
#include "linux/smp_lock.h"
#include "linux/ptrace.h"
#include "asm/ptrace.h"
#include "asm/pgtable.h"
#include "asm/tlbflush.h"
#include "asm/uaccess.h"
#include "user_util.h"
#include "kern_util.h"
#include "mem_user.h"
#include "kern.h"
#include "irq_user.h"
#include "tlb.h"
#include "os.h"
#include "choose-mode.h"
#include "mode_kern.h"
void flush_thread(void)
{
arch_flush_thread(&current->thread.arch);
CHOOSE_MODE(flush_thread_tt(), flush_thread_skas());
}
void start_thread(struct pt_regs *regs, unsigned long eip, unsigned long esp)
{
CHOOSE_MODE_PROC(start_thread_tt, start_thread_skas, regs, eip, esp);
}
#ifdef CONFIG_TTY_LOG
extern void log_exec(char **argv, void *tty);
#endif
static long execve1(char *file, char __user * __user *argv,
char __user *__user *env)
{
long error;
#ifdef CONFIG_TTY_LOG
struct tty_struct *tty;
mutex_lock(&tty_mutex);
tty = get_current_tty();
if (tty)
log_exec(argv, tty);
mutex_unlock(&tty_mutex);
#endif
error = do_execve(file, argv, env, &current->thread.regs);
if (error == 0){
task_lock(current);
current->ptrace &= ~PT_DTRACE;
#ifdef SUBARCH_EXECVE1
SUBARCH_EXECVE1(&current->thread.regs.regs);
#endif
task_unlock(current);
set_cmdline(current_cmd());
}
return(error);
}
long um_execve(char *file, char __user *__user *argv, char __user *__user *env)
{
long err;
err = execve1(file, argv, env);
if(!err)
do_longjmp(current->thread.exec_buf, 1);
return(err);
}
long sys_execve(char __user *file, char __user *__user *argv,
char __user *__user *env)
{
long error;
char *filename;
lock_kernel();
filename = getname(file);
error = PTR_ERR(filename);
if (IS_ERR(filename)) goto out;
error = execve1(filename, argv, env);
putname(filename);
out:
unlock_kernel();
return(error);
}

77
arch/um/kernel/exitcode.c Normal file
View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/init.h"
#include "linux/ctype.h"
#include "linux/proc_fs.h"
#include "asm/uaccess.h"
/* If read and write race, the read will still atomically read a valid
* value.
*/
int uml_exitcode = 0;
static int read_proc_exitcode(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len, val;
/* Save uml_exitcode in a local so that we don't need to guarantee
* that sprintf accesses it atomically.
*/
val = uml_exitcode;
len = sprintf(page, "%d\n", val);
len -= off;
if(len <= off+count) *eof = 1;
*start = page + off;
if(len > count) len = count;
if(len < 0) len = 0;
return(len);
}
static int write_proc_exitcode(struct file *file, const char __user *buffer,
unsigned long count, void *data)
{
char *end, buf[sizeof("nnnnn\0")];
int tmp;
if(copy_from_user(buf, buffer, count))
return(-EFAULT);
tmp = simple_strtol(buf, &end, 0);
if((*end != '\0') && !isspace(*end))
return(-EINVAL);
uml_exitcode = tmp;
return(count);
}
static int make_proc_exitcode(void)
{
struct proc_dir_entry *ent;
ent = create_proc_entry("exitcode", 0600, &proc_root);
if(ent == NULL){
printk(KERN_WARNING "make_proc_exitcode : Failed to register "
"/proc/exitcode\n");
return(0);
}
ent->read_proc = read_proc_exitcode;
ent->write_proc = write_proc_exitcode;
return(0);
}
__initcall(make_proc_exitcode);
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

View File

@@ -0,0 +1,23 @@
/*
* Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/module.h"
extern void __bb_init_func(void *) __attribute__((weak));
EXPORT_SYMBOL(__bb_init_func);
/* This is defined (and referred to in profiling stub code) only by some GCC
* versions in libgcov.
*
* Since SuSE backported the fix, we cannot handle it depending on GCC version.
* So, unconditinally export it. But also give it a weak declaration, which will
* be overriden by any other one.
*/
extern void __gcov_init(void *) __attribute__((weak));
EXPORT_SYMBOL(__gcov_init);
extern void __gcov_merge_add(void *) __attribute__((weak));
EXPORT_SYMBOL(__gcov_merge_add);

View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/module.h"
extern void mcount(void);
EXPORT_SYMBOL(mcount);
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/mm.h"
#include "linux/module.h"
#include "linux/sched.h"
#include "linux/init_task.h"
#include "linux/mqueue.h"
#include "asm/uaccess.h"
#include "asm/pgtable.h"
#include "user_util.h"
#include "mem_user.h"
#include "os.h"
static struct fs_struct init_fs = INIT_FS;
struct mm_struct init_mm = INIT_MM(init_mm);
static struct files_struct init_files = INIT_FILES;
static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
EXPORT_SYMBOL(init_mm);
/*
* Initial task structure.
*
* All other task structs will be allocated on slabs in fork.c
*/
struct task_struct init_task = INIT_TASK(init_task);
EXPORT_SYMBOL(init_task);
/*
* Initial thread structure.
*
* We need to make sure that this is 16384-byte aligned due to the
* way process stacks are handled. This is done by having a special
* "init_task" linker map entry..
*/
union thread_union init_thread_union
__attribute__((__section__(".data.init_task"))) =
{ INIT_THREAD_INFO(init_task) };
void unprotect_stack(unsigned long stack)
{
os_protect_memory((void *) stack, (1 << CONFIG_KERNEL_STACK_ORDER) * PAGE_SIZE,
1, 1, 0);
}
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

78
arch/um/kernel/initrd.c Normal file
View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/init.h"
#include "linux/bootmem.h"
#include "linux/initrd.h"
#include "asm/types.h"
#include "user_util.h"
#include "kern_util.h"
#include "initrd.h"
#include "init.h"
#include "os.h"
/* Changed by uml_initrd_setup, which is a setup */
static char *initrd __initdata = NULL;
static int __init read_initrd(void)
{
void *area;
long long size;
int err;
if(initrd == NULL) return 0;
err = os_file_size(initrd, &size);
if(err) return 0;
area = alloc_bootmem(size);
if(area == NULL) return 0;
if(load_initrd(initrd, area, size) == -1) return 0;
initrd_start = (unsigned long) area;
initrd_end = initrd_start + size;
return 0;
}
__uml_postsetup(read_initrd);
static int __init uml_initrd_setup(char *line, int *add)
{
initrd = line;
return 0;
}
__uml_setup("initrd=", uml_initrd_setup,
"initrd=<initrd image>\n"
" This is used to boot UML from an initrd image. The argument is the\n"
" name of the file containing the image.\n\n"
);
int load_initrd(char *filename, void *buf, int size)
{
int fd, n;
fd = os_open_file(filename, of_read(OPENFLAGS()), 0);
if(fd < 0){
printk("Opening '%s' failed - err = %d\n", filename, -fd);
return(-1);
}
n = os_read_file(fd, buf, size);
if(n != size){
printk("Read of %d bytes from '%s' failed, err = %d\n", size,
filename, -n);
return(-1);
}
os_close_file(fd);
return(0);
}
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

456
arch/um/kernel/irq.c Normal file
View File

@@ -0,0 +1,456 @@
/*
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
* Derived (i.e. mostly copied) from arch/i386/kernel/irq.c:
* Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar
*/
#include "linux/kernel.h"
#include "linux/module.h"
#include "linux/smp.h"
#include "linux/kernel_stat.h"
#include "linux/interrupt.h"
#include "linux/random.h"
#include "linux/slab.h"
#include "linux/file.h"
#include "linux/proc_fs.h"
#include "linux/init.h"
#include "linux/seq_file.h"
#include "linux/profile.h"
#include "linux/hardirq.h"
#include "asm/irq.h"
#include "asm/hw_irq.h"
#include "asm/atomic.h"
#include "asm/signal.h"
#include "asm/system.h"
#include "asm/errno.h"
#include "asm/uaccess.h"
#include "user_util.h"
#include "kern_util.h"
#include "irq_user.h"
#include "irq_kern.h"
#include "os.h"
#include "sigio.h"
#include "um_malloc.h"
#include "misc_constants.h"
/*
* Generic, controller-independent functions:
*/
int show_interrupts(struct seq_file *p, void *v)
{
int i = *(loff_t *) v, j;
struct irqaction * action;
unsigned long flags;
if (i == 0) {
seq_printf(p, " ");
for_each_online_cpu(j)
seq_printf(p, "CPU%d ",j);
seq_putc(p, '\n');
}
if (i < NR_IRQS) {
spin_lock_irqsave(&irq_desc[i].lock, flags);
action = irq_desc[i].action;
if (!action)
goto skip;
seq_printf(p, "%3d: ",i);
#ifndef CONFIG_SMP
seq_printf(p, "%10u ", kstat_irqs(i));
#else
for_each_online_cpu(j)
seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]);
#endif
seq_printf(p, " %14s", irq_desc[i].chip->typename);
seq_printf(p, " %s", action->name);
for (action=action->next; action; action = action->next)
seq_printf(p, ", %s", action->name);
seq_putc(p, '\n');
skip:
spin_unlock_irqrestore(&irq_desc[i].lock, flags);
} else if (i == NR_IRQS) {
seq_putc(p, '\n');
}
return 0;
}
static struct irq_fd *active_fds = NULL;
static struct irq_fd **last_irq_ptr = &active_fds;
extern void free_irqs(void);
void sigio_handler(int sig, union uml_pt_regs *regs)
{
struct irq_fd *irq_fd;
int n;
if (smp_sigio_handler())
return;
while (1) {
n = os_waiting_for_events(active_fds);
if (n <= 0) {
if(n == -EINTR) continue;
else break;
}
for (irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next) {
if (irq_fd->current_events != 0) {
irq_fd->current_events = 0;
do_IRQ(irq_fd->irq, regs);
}
}
}
free_irqs();
}
static DEFINE_SPINLOCK(irq_lock);
int activate_fd(int irq, int fd, int type, void *dev_id)
{
struct pollfd *tmp_pfd;
struct irq_fd *new_fd, *irq_fd;
unsigned long flags;
int pid, events, err, n;
pid = os_getpid();
err = os_set_fd_async(fd, pid);
if (err < 0)
goto out;
err = -ENOMEM;
new_fd = kmalloc(sizeof(struct irq_fd), GFP_KERNEL);
if (new_fd == NULL)
goto out;
if (type == IRQ_READ)
events = UM_POLLIN | UM_POLLPRI;
else
events = UM_POLLOUT;
*new_fd = ((struct irq_fd) { .next = NULL,
.id = dev_id,
.fd = fd,
.type = type,
.irq = irq,
.pid = pid,
.events = events,
.current_events = 0 } );
err = -EBUSY;
spin_lock_irqsave(&irq_lock, flags);
for (irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next) {
if ((irq_fd->fd == fd) && (irq_fd->type == type)) {
printk("Registering fd %d twice\n", fd);
printk("Irqs : %d, %d\n", irq_fd->irq, irq);
printk("Ids : 0x%p, 0x%p\n", irq_fd->id, dev_id);
goto out_unlock;
}
}
if (type == IRQ_WRITE)
fd = -1;
tmp_pfd = NULL;
n = 0;
while (1) {
n = os_create_pollfd(fd, events, tmp_pfd, n);
if (n == 0)
break;
/* n > 0
* It means we couldn't put new pollfd to current pollfds
* and tmp_fds is NULL or too small for new pollfds array.
* Needed size is equal to n as minimum.
*
* Here we have to drop the lock in order to call
* kmalloc, which might sleep.
* If something else came in and changed the pollfds array
* so we will not be able to put new pollfd struct to pollfds
* then we free the buffer tmp_fds and try again.
*/
spin_unlock_irqrestore(&irq_lock, flags);
kfree(tmp_pfd);
tmp_pfd = kmalloc(n, GFP_KERNEL);
if (tmp_pfd == NULL)
goto out_kfree;
spin_lock_irqsave(&irq_lock, flags);
}
*last_irq_ptr = new_fd;
last_irq_ptr = &new_fd->next;
spin_unlock_irqrestore(&irq_lock, flags);
/* This calls activate_fd, so it has to be outside the critical
* section.
*/
maybe_sigio_broken(fd, (type == IRQ_READ));
return 0;
out_unlock:
spin_unlock_irqrestore(&irq_lock, flags);
out_kfree:
kfree(new_fd);
out:
return err;
}
static void free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg)
{
unsigned long flags;
spin_lock_irqsave(&irq_lock, flags);
os_free_irq_by_cb(test, arg, active_fds, &last_irq_ptr);
spin_unlock_irqrestore(&irq_lock, flags);
}
struct irq_and_dev {
int irq;
void *dev;
};
static int same_irq_and_dev(struct irq_fd *irq, void *d)
{
struct irq_and_dev *data = d;
return ((irq->irq == data->irq) && (irq->id == data->dev));
}
void free_irq_by_irq_and_dev(unsigned int irq, void *dev)
{
struct irq_and_dev data = ((struct irq_and_dev) { .irq = irq,
.dev = dev });
free_irq_by_cb(same_irq_and_dev, &data);
}
static int same_fd(struct irq_fd *irq, void *fd)
{
return (irq->fd == *((int *)fd));
}
void free_irq_by_fd(int fd)
{
free_irq_by_cb(same_fd, &fd);
}
static struct irq_fd *find_irq_by_fd(int fd, int irqnum, int *index_out)
{
struct irq_fd *irq;
int i = 0;
int fdi;
for (irq = active_fds; irq != NULL; irq = irq->next) {
if ((irq->fd == fd) && (irq->irq == irqnum))
break;
i++;
}
if (irq == NULL) {
printk("find_irq_by_fd doesn't have descriptor %d\n", fd);
goto out;
}
fdi = os_get_pollfd(i);
if ((fdi != -1) && (fdi != fd)) {
printk("find_irq_by_fd - mismatch between active_fds and "
"pollfds, fd %d vs %d, need %d\n", irq->fd,
fdi, fd);
irq = NULL;
goto out;
}
*index_out = i;
out:
return irq;
}
void reactivate_fd(int fd, int irqnum)
{
struct irq_fd *irq;
unsigned long flags;
int i;
spin_lock_irqsave(&irq_lock, flags);
irq = find_irq_by_fd(fd, irqnum, &i);
if (irq == NULL) {
spin_unlock_irqrestore(&irq_lock, flags);
return;
}
os_set_pollfd(i, irq->fd);
spin_unlock_irqrestore(&irq_lock, flags);
add_sigio_fd(fd);
}
void deactivate_fd(int fd, int irqnum)
{
struct irq_fd *irq;
unsigned long flags;
int i;
spin_lock_irqsave(&irq_lock, flags);
irq = find_irq_by_fd(fd, irqnum, &i);
if(irq == NULL){
spin_unlock_irqrestore(&irq_lock, flags);
return;
}
os_set_pollfd(i, -1);
spin_unlock_irqrestore(&irq_lock, flags);
ignore_sigio_fd(fd);
}
int deactivate_all_fds(void)
{
struct irq_fd *irq;
int err;
for (irq = active_fds; irq != NULL; irq = irq->next) {
err = os_clear_fd_async(irq->fd);
if (err)
return err;
}
/* If there is a signal already queued, after unblocking ignore it */
os_set_ioignore();
return 0;
}
#ifdef CONFIG_MODE_TT
void forward_interrupts(int pid)
{
struct irq_fd *irq;
unsigned long flags;
int err;
spin_lock_irqsave(&irq_lock, flags);
for (irq = active_fds; irq != NULL; irq = irq->next) {
err = os_set_owner(irq->fd, pid);
if (err < 0) {
/* XXX Just remove the irq rather than
* print out an infinite stream of these
*/
printk("Failed to forward %d to pid %d, err = %d\n",
irq->fd, pid, -err);
}
irq->pid = pid;
}
spin_unlock_irqrestore(&irq_lock, flags);
}
#endif
/*
* do_IRQ handles all normal device IRQ's (the special
* SMP cross-CPU interrupts have their own specific
* handlers).
*/
unsigned int do_IRQ(int irq, union uml_pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs((struct pt_regs *)regs);
irq_enter();
__do_IRQ(irq);
irq_exit();
set_irq_regs(old_regs);
return 1;
}
int um_request_irq(unsigned int irq, int fd, int type,
irq_handler_t handler,
unsigned long irqflags, const char * devname,
void *dev_id)
{
int err;
err = request_irq(irq, handler, irqflags, devname, dev_id);
if (err)
return err;
if (fd != -1)
err = activate_fd(irq, fd, type, dev_id);
return err;
}
EXPORT_SYMBOL(um_request_irq);
EXPORT_SYMBOL(reactivate_fd);
/* hw_interrupt_type must define (startup || enable) &&
* (shutdown || disable) && end */
static void dummy(unsigned int irq)
{
}
/* This is used for everything else than the timer. */
static struct hw_interrupt_type normal_irq_type = {
.typename = "SIGIO",
.release = free_irq_by_irq_and_dev,
.disable = dummy,
.enable = dummy,
.ack = dummy,
.end = dummy
};
static struct hw_interrupt_type SIGVTALRM_irq_type = {
.typename = "SIGVTALRM",
.release = free_irq_by_irq_and_dev,
.shutdown = dummy, /* never called */
.disable = dummy,
.enable = dummy,
.ack = dummy,
.end = dummy
};
void __init init_IRQ(void)
{
int i;
irq_desc[TIMER_IRQ].status = IRQ_DISABLED;
irq_desc[TIMER_IRQ].action = NULL;
irq_desc[TIMER_IRQ].depth = 1;
irq_desc[TIMER_IRQ].chip = &SIGVTALRM_irq_type;
enable_irq(TIMER_IRQ);
for (i = 1; i < NR_IRQS; i++) {
irq_desc[i].status = IRQ_DISABLED;
irq_desc[i].action = NULL;
irq_desc[i].depth = 1;
irq_desc[i].chip = &normal_irq_type;
enable_irq(i);
}
}
int init_aio_irq(int irq, char *name, irq_handler_t handler)
{
int fds[2], err;
err = os_pipe(fds, 1, 1);
if (err) {
printk("init_aio_irq - os_pipe failed, err = %d\n", -err);
goto out;
}
err = um_request_irq(irq, fds[0], IRQ_READ, handler,
IRQF_DISABLED | IRQF_SAMPLE_RANDOM, name,
(void *) (long) fds[0]);
if (err) {
printk("init_aio_irq - : um_request_irq failed, err = %d\n",
err);
goto out_close;
}
err = fds[1];
goto out;
out_close:
os_close_file(fds[0]);
os_close_file(fds[1]);
out:
return err;
}

101
arch/um/kernel/ksyms.c Normal file
View File

@@ -0,0 +1,101 @@
/*
* Copyright (C) 2001 - 2004 Jeff Dike (jdike@addtoit.com)
* Licensed under the GPL
*/
#include "linux/module.h"
#include "linux/string.h"
#include "linux/smp_lock.h"
#include "linux/spinlock.h"
#include "linux/highmem.h"
#include "asm/current.h"
#include "asm/processor.h"
#include "asm/unistd.h"
#include "asm/pgalloc.h"
#include "asm/pgtable.h"
#include "asm/page.h"
#include "asm/tlbflush.h"
#include "kern_util.h"
#include "user_util.h"
#include "mem_user.h"
#include "os.h"
EXPORT_SYMBOL(uml_physmem);
EXPORT_SYMBOL(set_signals);
EXPORT_SYMBOL(get_signals);
EXPORT_SYMBOL(kernel_thread);
EXPORT_SYMBOL(sys_waitpid);
EXPORT_SYMBOL(task_size);
EXPORT_SYMBOL(flush_tlb_range);
EXPORT_SYMBOL(host_task_size);
EXPORT_SYMBOL(arch_validate);
EXPORT_SYMBOL(get_kmem_end);
EXPORT_SYMBOL(high_physmem);
EXPORT_SYMBOL(empty_zero_page);
EXPORT_SYMBOL(um_virt_to_phys);
EXPORT_SYMBOL(mode_tt);
EXPORT_SYMBOL(handle_page_fault);
EXPORT_SYMBOL(find_iomem);
#ifdef CONFIG_MODE_TT
EXPORT_SYMBOL(stop);
EXPORT_SYMBOL(strncpy_from_user_tt);
EXPORT_SYMBOL(copy_from_user_tt);
EXPORT_SYMBOL(copy_to_user_tt);
#endif
#ifdef CONFIG_MODE_SKAS
EXPORT_SYMBOL(strnlen_user_skas);
EXPORT_SYMBOL(strncpy_from_user_skas);
EXPORT_SYMBOL(copy_to_user_skas);
EXPORT_SYMBOL(copy_from_user_skas);
EXPORT_SYMBOL(clear_user_skas);
#endif
EXPORT_SYMBOL(uml_strdup);
EXPORT_SYMBOL(os_stat_fd);
EXPORT_SYMBOL(os_stat_file);
EXPORT_SYMBOL(os_access);
EXPORT_SYMBOL(os_print_error);
EXPORT_SYMBOL(os_get_exec_close);
EXPORT_SYMBOL(os_set_exec_close);
EXPORT_SYMBOL(os_getpid);
EXPORT_SYMBOL(os_open_file);
EXPORT_SYMBOL(os_read_file);
EXPORT_SYMBOL(os_write_file);
EXPORT_SYMBOL(os_seek_file);
EXPORT_SYMBOL(os_lock_file);
EXPORT_SYMBOL(os_ioctl_generic);
EXPORT_SYMBOL(os_pipe);
EXPORT_SYMBOL(os_file_type);
EXPORT_SYMBOL(os_file_mode);
EXPORT_SYMBOL(os_file_size);
EXPORT_SYMBOL(os_flush_stdout);
EXPORT_SYMBOL(os_close_file);
EXPORT_SYMBOL(os_set_fd_async);
EXPORT_SYMBOL(os_set_fd_block);
EXPORT_SYMBOL(helper_wait);
EXPORT_SYMBOL(os_shutdown_socket);
EXPORT_SYMBOL(os_create_unix_socket);
EXPORT_SYMBOL(os_connect_socket);
EXPORT_SYMBOL(os_accept_connection);
EXPORT_SYMBOL(os_rcv_fd);
EXPORT_SYMBOL(run_helper);
EXPORT_SYMBOL(start_thread);
EXPORT_SYMBOL(dump_thread);
EXPORT_SYMBOL(do_gettimeofday);
EXPORT_SYMBOL(do_settimeofday);
#ifdef CONFIG_SMP
/* required for SMP */
extern void FASTCALL( __write_lock_failed(rwlock_t *rw));
EXPORT_SYMBOL(__write_lock_failed);
extern void FASTCALL( __read_lock_failed(rwlock_t *rw));
EXPORT_SYMBOL(__read_lock_failed);
#endif

370
arch/um/kernel/mem.c Normal file
View File

@@ -0,0 +1,370 @@
/*
* Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
* Licensed under the GPL
*/
#include "linux/stddef.h"
#include "linux/kernel.h"
#include "linux/mm.h"
#include "linux/bootmem.h"
#include "linux/swap.h"
#include "linux/highmem.h"
#include "linux/gfp.h"
#include "asm/page.h"
#include "asm/fixmap.h"
#include "asm/pgalloc.h"
#include "user_util.h"
#include "kern_util.h"
#include "kern.h"
#include "mem_user.h"
#include "uml_uaccess.h"
#include "os.h"
#include "linux/types.h"
#include "linux/string.h"
#include "init.h"
#include "kern_constants.h"
/* allocated in paging_init, zeroed in mem_init, and unchanged thereafter */
unsigned long *empty_zero_page = NULL;
/* allocated in paging_init and unchanged thereafter */
unsigned long *empty_bad_page = NULL;
pgd_t swapper_pg_dir[PTRS_PER_PGD];
unsigned long long highmem;
int kmalloc_ok = 0;
static unsigned long brk_end;
void unmap_physmem(void)
{
os_unmap_memory((void *) brk_end, uml_reserved - brk_end);
}
static void map_cb(void *unused)
{
map_memory(brk_end, __pa(brk_end), uml_reserved - brk_end, 1, 1, 0);
}
#ifdef CONFIG_HIGHMEM
static void setup_highmem(unsigned long highmem_start,
unsigned long highmem_len)
{
struct page *page;
unsigned long highmem_pfn;
int i;
highmem_pfn = __pa(highmem_start) >> PAGE_SHIFT;
for(i = 0; i < highmem_len >> PAGE_SHIFT; i++){
page = &mem_map[highmem_pfn + i];
ClearPageReserved(page);
init_page_count(page);
__free_page(page);
}
}
#endif
void mem_init(void)
{
/* clear the zero-page */
memset((void *) empty_zero_page, 0, PAGE_SIZE);
/* Map in the area just after the brk now that kmalloc is about
* to be turned on.
*/
brk_end = (unsigned long) UML_ROUND_UP(sbrk(0));
map_cb(NULL);
initial_thread_cb(map_cb, NULL);
free_bootmem(__pa(brk_end), uml_reserved - brk_end);
uml_reserved = brk_end;
/* this will put all low memory onto the freelists */
totalram_pages = free_all_bootmem();
max_low_pfn = totalram_pages;
#ifdef CONFIG_HIGHMEM
totalhigh_pages = highmem >> PAGE_SHIFT;
totalram_pages += totalhigh_pages;
#endif
num_physpages = totalram_pages;
max_pfn = totalram_pages;
printk(KERN_INFO "Memory: %luk available\n",
(unsigned long) nr_free_pages() << (PAGE_SHIFT-10));
kmalloc_ok = 1;
#ifdef CONFIG_HIGHMEM
setup_highmem(end_iomem, highmem);
#endif
}
/*
* Create a page table and place a pointer to it in a middle page
* directory entry.
*/
static void __init one_page_table_init(pmd_t *pmd)
{
if (pmd_none(*pmd)) {
pte_t *pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);
set_pmd(pmd, __pmd(_KERNPG_TABLE +
(unsigned long) __pa(pte)));
if (pte != pte_offset_kernel(pmd, 0))
BUG();
}
}
static void __init one_md_table_init(pud_t *pud)
{
#ifdef CONFIG_3_LEVEL_PGTABLES
pmd_t *pmd_table = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE);
set_pud(pud, __pud(_KERNPG_TABLE + (unsigned long) __pa(pmd_table)));
if (pmd_table != pmd_offset(pud, 0))
BUG();
#endif
}
static void __init fixrange_init(unsigned long start, unsigned long end,
pgd_t *pgd_base)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
int i, j;
unsigned long vaddr;
vaddr = start;
i = pgd_index(vaddr);
j = pmd_index(vaddr);
pgd = pgd_base + i;
for ( ; (i < PTRS_PER_PGD) && (vaddr < end); pgd++, i++) {
pud = pud_offset(pgd, vaddr);
if (pud_none(*pud))
one_md_table_init(pud);
pmd = pmd_offset(pud, vaddr);
for (; (j < PTRS_PER_PMD) && (vaddr != end); pmd++, j++) {
one_page_table_init(pmd);
vaddr += PMD_SIZE;
}
j = 0;
}
}
#ifdef CONFIG_HIGHMEM
pte_t *kmap_pte;
pgprot_t kmap_prot;
#define kmap_get_fixmap_pte(vaddr) \
pte_offset_kernel(pmd_offset(pud_offset(pgd_offset_k(vaddr), (vaddr)),\
(vaddr)), (vaddr))
static void __init kmap_init(void)
{
unsigned long kmap_vstart;
/* cache the first kmap pte */
kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN);
kmap_pte = kmap_get_fixmap_pte(kmap_vstart);
kmap_prot = PAGE_KERNEL;
}
static void init_highmem(void)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
unsigned long vaddr;
/*
* Permanent kmaps:
*/
vaddr = PKMAP_BASE;
fixrange_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, swapper_pg_dir);
pgd = swapper_pg_dir + pgd_index(vaddr);
pud = pud_offset(pgd, vaddr);
pmd = pmd_offset(pud, vaddr);
pte = pte_offset_kernel(pmd, vaddr);
pkmap_page_table = pte;
kmap_init();
}
#endif /* CONFIG_HIGHMEM */
static void __init fixaddr_user_init( void)
{
#ifdef CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA
long size = FIXADDR_USER_END - FIXADDR_USER_START;
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
unsigned long paddr, vaddr = FIXADDR_USER_START;
if ( ! size )
return;
fixrange_init( FIXADDR_USER_START, FIXADDR_USER_END, swapper_pg_dir);
paddr = (unsigned long)alloc_bootmem_low_pages( size);
memcpy( (void *)paddr, (void *)FIXADDR_USER_START, size);
paddr = __pa(paddr);
for ( ; size > 0; size-=PAGE_SIZE, vaddr+=PAGE_SIZE, paddr+=PAGE_SIZE){
pgd = swapper_pg_dir + pgd_index(vaddr);
pud = pud_offset(pgd, vaddr);
pmd = pmd_offset(pud, vaddr);
pte = pte_offset_kernel(pmd, vaddr);
pte_set_val( (*pte), paddr, PAGE_READONLY);
}
#endif
}
void paging_init(void)
{
unsigned long zones_size[MAX_NR_ZONES], vaddr;
int i;
empty_zero_page = (unsigned long *) alloc_bootmem_low_pages(PAGE_SIZE);
empty_bad_page = (unsigned long *) alloc_bootmem_low_pages(PAGE_SIZE);
for(i = 0; i < ARRAY_SIZE(zones_size); i++)
zones_size[i] = 0;
zones_size[ZONE_NORMAL] = (end_iomem >> PAGE_SHIFT) -
(uml_physmem >> PAGE_SHIFT);
#ifdef CONFIG_HIGHMEM
zones_size[ZONE_HIGHMEM] = highmem >> PAGE_SHIFT;
#endif
free_area_init(zones_size);
/*
* Fixed mappings, only the page table structure has to be
* created - mappings will be set by set_fixmap():
*/
vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;
fixrange_init(vaddr, FIXADDR_TOP, swapper_pg_dir);
fixaddr_user_init();
#ifdef CONFIG_HIGHMEM
init_highmem();
#endif
}
struct page *arch_validate(struct page *page, gfp_t mask, int order)
{
unsigned long addr, zero = 0;
int i;
again:
if(page == NULL)
return page;
if(PageHighMem(page))
return page;
addr = (unsigned long) page_address(page);
for(i = 0; i < (1 << order); i++){
current->thread.fault_addr = (void *) addr;
if(__do_copy_to_user((void __user *) addr, &zero,
sizeof(zero),
&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));
init_page_count(virt_to_page(start));
free_page(start);
totalram_pages++;
}
}
#endif
void show_mem(void)
{
int pfn, total = 0, reserved = 0;
int shared = 0, cached = 0;
int highmem = 0;
struct page *page;
printk("Mem-info:\n");
show_free_areas();
printk("Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10));
pfn = max_mapnr;
while(pfn-- > 0) {
page = pfn_to_page(pfn);
total++;
if(PageHighMem(page))
highmem++;
if(PageReserved(page))
reserved++;
else if(PageSwapCache(page))
cached++;
else if(page_count(page))
shared += page_count(page) - 1;
}
printk("%d pages of RAM\n", total);
printk("%d pages of HIGHMEM\n", highmem);
printk("%d reserved pages\n", reserved);
printk("%d pages shared\n", shared);
printk("%d pages swap cached\n", cached);
}
/*
* Allocate and free page tables.
*/
pgd_t *pgd_alloc(struct mm_struct *mm)
{
pgd_t *pgd = (pgd_t *)__get_free_page(GFP_KERNEL);
if (pgd) {
memset(pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
memcpy(pgd + USER_PTRS_PER_PGD,
swapper_pg_dir + USER_PTRS_PER_PGD,
(PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
}
return pgd;
}
void pgd_free(pgd_t *pgd)
{
free_page((unsigned long) pgd);
}
pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address)
{
pte_t *pte;
pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO);
return pte;
}
struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address)
{
struct page *pte;
pte = alloc_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO);
return pte;
}

458
arch/um/kernel/physmem.c Normal file
View File

@@ -0,0 +1,458 @@
/*
* Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
* Licensed under the GPL
*/
#include "linux/mm.h"
#include "linux/rbtree.h"
#include "linux/slab.h"
#include "linux/vmalloc.h"
#include "linux/bootmem.h"
#include "linux/module.h"
#include "linux/pfn.h"
#include "asm/types.h"
#include "asm/pgtable.h"
#include "kern_util.h"
#include "user_util.h"
#include "mode_kern.h"
#include "mem.h"
#include "mem_user.h"
#include "os.h"
#include "kern.h"
#include "init.h"
struct phys_desc {
struct rb_node rb;
int fd;
__u64 offset;
void *virt;
unsigned long phys;
struct list_head list;
};
static struct rb_root phys_mappings = RB_ROOT;
static struct rb_node **find_rb(void *virt)
{
struct rb_node **n = &phys_mappings.rb_node;
struct phys_desc *d;
while(*n != NULL){
d = rb_entry(*n, struct phys_desc, rb);
if(d->virt == virt)
return n;
if(d->virt > virt)
n = &(*n)->rb_left;
else
n = &(*n)->rb_right;
}
return n;
}
static struct phys_desc *find_phys_mapping(void *virt)
{
struct rb_node **n = find_rb(virt);
if(*n == NULL)
return NULL;
return rb_entry(*n, struct phys_desc, rb);
}
static void insert_phys_mapping(struct phys_desc *desc)
{
struct rb_node **n = find_rb(desc->virt);
if(*n != NULL)
panic("Physical remapping for %p already present",
desc->virt);
rb_link_node(&desc->rb, rb_parent(*n), n);
rb_insert_color(&desc->rb, &phys_mappings);
}
LIST_HEAD(descriptor_mappings);
struct desc_mapping {
int fd;
struct list_head list;
struct list_head pages;
};
static struct desc_mapping *find_mapping(int fd)
{
struct desc_mapping *desc;
struct list_head *ele;
list_for_each(ele, &descriptor_mappings){
desc = list_entry(ele, struct desc_mapping, list);
if(desc->fd == fd)
return desc;
}
return NULL;
}
static struct desc_mapping *descriptor_mapping(int fd)
{
struct desc_mapping *desc;
desc = find_mapping(fd);
if(desc != NULL)
return desc;
desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
if(desc == NULL)
return NULL;
*desc = ((struct desc_mapping)
{ .fd = fd,
.list = LIST_HEAD_INIT(desc->list),
.pages = LIST_HEAD_INIT(desc->pages) });
list_add(&desc->list, &descriptor_mappings);
return desc;
}
int physmem_subst_mapping(void *virt, int fd, __u64 offset, int w)
{
struct desc_mapping *fd_maps;
struct phys_desc *desc;
unsigned long phys;
int err;
fd_maps = descriptor_mapping(fd);
if(fd_maps == NULL)
return -ENOMEM;
phys = __pa(virt);
desc = find_phys_mapping(virt);
if(desc != NULL)
panic("Address 0x%p is already substituted\n", virt);
err = -ENOMEM;
desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
if(desc == NULL)
goto out;
*desc = ((struct phys_desc)
{ .fd = fd,
.offset = offset,
.virt = virt,
.phys = __pa(virt),
.list = LIST_HEAD_INIT(desc->list) });
insert_phys_mapping(desc);
list_add(&desc->list, &fd_maps->pages);
virt = (void *) ((unsigned long) virt & PAGE_MASK);
err = os_map_memory(virt, fd, offset, PAGE_SIZE, 1, w, 0);
if(!err)
goto out;
rb_erase(&desc->rb, &phys_mappings);
kfree(desc);
out:
return err;
}
static int physmem_fd = -1;
static void remove_mapping(struct phys_desc *desc)
{
void *virt = desc->virt;
int err;
rb_erase(&desc->rb, &phys_mappings);
list_del(&desc->list);
kfree(desc);
err = os_map_memory(virt, physmem_fd, __pa(virt), PAGE_SIZE, 1, 1, 0);
if(err)
panic("Failed to unmap block device page from physical memory, "
"errno = %d", -err);
}
int physmem_remove_mapping(void *virt)
{
struct phys_desc *desc;
virt = (void *) ((unsigned long) virt & PAGE_MASK);
desc = find_phys_mapping(virt);
if(desc == NULL)
return 0;
remove_mapping(desc);
return 1;
}
void physmem_forget_descriptor(int fd)
{
struct desc_mapping *desc;
struct phys_desc *page;
struct list_head *ele, *next;
__u64 offset;
void *addr;
int err;
desc = find_mapping(fd);
if(desc == NULL)
return;
list_for_each_safe(ele, next, &desc->pages){
page = list_entry(ele, struct phys_desc, list);
offset = page->offset;
addr = page->virt;
remove_mapping(page);
err = os_seek_file(fd, offset);
if(err)
panic("physmem_forget_descriptor - failed to seek "
"to %lld in fd %d, error = %d\n",
offset, fd, -err);
err = os_read_file(fd, addr, PAGE_SIZE);
if(err < 0)
panic("physmem_forget_descriptor - failed to read "
"from fd %d to 0x%p, error = %d\n",
fd, addr, -err);
}
list_del(&desc->list);
kfree(desc);
}
EXPORT_SYMBOL(physmem_forget_descriptor);
EXPORT_SYMBOL(physmem_remove_mapping);
EXPORT_SYMBOL(physmem_subst_mapping);
void arch_free_page(struct page *page, int order)
{
void *virt;
int i;
for(i = 0; i < (1 << order); i++){
virt = __va(page_to_phys(page + i));
physmem_remove_mapping(virt);
}
}
int is_remapped(void *virt)
{
struct phys_desc *desc = find_phys_mapping(virt);
return desc != NULL;
}
/* Changed during early boot */
unsigned long high_physmem;
extern unsigned long long physmem_size;
int init_maps(unsigned long physmem, unsigned long iomem, unsigned long highmem)
{
struct page *p, *map;
unsigned long phys_len, phys_pages, highmem_len, highmem_pages;
unsigned long iomem_len, iomem_pages, total_len, total_pages;
int i;
phys_pages = physmem >> PAGE_SHIFT;
phys_len = phys_pages * sizeof(struct page);
iomem_pages = iomem >> PAGE_SHIFT;
iomem_len = iomem_pages * sizeof(struct page);
highmem_pages = highmem >> PAGE_SHIFT;
highmem_len = highmem_pages * sizeof(struct page);
total_pages = phys_pages + iomem_pages + highmem_pages;
total_len = phys_len + iomem_len + highmem_len;
if(kmalloc_ok){
map = kmalloc(total_len, GFP_KERNEL);
if(map == NULL)
map = vmalloc(total_len);
}
else map = alloc_bootmem_low_pages(total_len);
if(map == NULL)
return -ENOMEM;
for(i = 0; i < total_pages; i++){
p = &map[i];
memset(p, 0, sizeof(struct page));
SetPageReserved(p);
INIT_LIST_HEAD(&p->lru);
}
max_mapnr = total_pages;
return 0;
}
/* Changed during early boot */
static unsigned long kmem_top = 0;
unsigned long get_kmem_end(void)
{
if(kmem_top == 0)
kmem_top = CHOOSE_MODE(kmem_end_tt, kmem_end_skas);
return kmem_top;
}
void map_memory(unsigned long virt, unsigned long phys, unsigned long len,
int r, int w, int x)
{
__u64 offset;
int fd, err;
fd = phys_mapping(phys, &offset);
err = os_map_memory((void *) virt, fd, offset, len, r, w, x);
if(err) {
if(err == -ENOMEM)
printk("try increasing the host's "
"/proc/sys/vm/max_map_count to <physical "
"memory size>/4096\n");
panic("map_memory(0x%lx, %d, 0x%llx, %ld, %d, %d, %d) failed, "
"err = %d\n", virt, fd, offset, len, r, w, x, err);
}
}
extern int __syscall_stub_start;
void setup_physmem(unsigned long start, unsigned long reserve_end,
unsigned long len, unsigned long long highmem)
{
unsigned long reserve = reserve_end - start;
int pfn = PFN_UP(__pa(reserve_end));
int delta = (len - reserve) >> PAGE_SHIFT;
int err, offset, bootmap_size;
physmem_fd = create_mem_file(len + highmem);
offset = uml_reserved - uml_physmem;
err = os_map_memory((void *) uml_reserved, physmem_fd, offset,
len - offset, 1, 1, 0);
if(err < 0){
os_print_error(err, "Mapping memory");
exit(1);
}
/* Special kludge - This page will be mapped in to userspace processes
* from physmem_fd, so it needs to be written out there.
*/
os_seek_file(physmem_fd, __pa(&__syscall_stub_start));
os_write_file(physmem_fd, &__syscall_stub_start, PAGE_SIZE);
bootmap_size = init_bootmem(pfn, pfn + delta);
free_bootmem(__pa(reserve_end) + bootmap_size,
len - bootmap_size - reserve);
}
int phys_mapping(unsigned long phys, __u64 *offset_out)
{
struct phys_desc *desc = find_phys_mapping(__va(phys & PAGE_MASK));
int fd = -1;
if(desc != NULL){
fd = desc->fd;
*offset_out = desc->offset;
}
else if(phys < physmem_size){
fd = physmem_fd;
*offset_out = phys;
}
else if(phys < __pa(end_iomem)){
struct iomem_region *region = iomem_regions;
while(region != NULL){
if((phys >= region->phys) &&
(phys < region->phys + region->size)){
fd = region->fd;
*offset_out = phys - region->phys;
break;
}
region = region->next;
}
}
else if(phys < __pa(end_iomem) + highmem){
fd = physmem_fd;
*offset_out = phys - iomem_size;
}
return fd;
}
static int __init uml_mem_setup(char *line, int *add)
{
char *retptr;
physmem_size = memparse(line,&retptr);
return 0;
}
__uml_setup("mem=", uml_mem_setup,
"mem=<Amount of desired ram>\n"
" This controls how much \"physical\" memory the kernel allocates\n"
" for the system. The size is specified as a number followed by\n"
" one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n"
" This is not related to the amount of memory in the host. It can\n"
" be more, and the excess, if it's ever used, will just be swapped out.\n"
" Example: mem=64M\n\n"
);
extern int __init parse_iomem(char *str, int *add);
__uml_setup("iomem=", parse_iomem,
"iomem=<name>,<file>\n"
" Configure <file> as an IO memory region named <name>.\n\n"
);
/*
* This list is constructed in parse_iomem and addresses filled in in
* setup_iomem, both of which run during early boot. Afterwards, it's
* unchanged.
*/
struct iomem_region *iomem_regions = NULL;
/* Initialized in parse_iomem */
int iomem_size = 0;
unsigned long find_iomem(char *driver, unsigned long *len_out)
{
struct iomem_region *region = iomem_regions;
while(region != NULL){
if(!strcmp(region->driver, driver)){
*len_out = region->size;
return region->virt;
}
region = region->next;
}
return 0;
}
int setup_iomem(void)
{
struct iomem_region *region = iomem_regions;
unsigned long iomem_start = high_physmem + PAGE_SIZE;
int err;
while(region != NULL){
err = os_map_memory((void *) iomem_start, region->fd, 0,
region->size, 1, 1, 0);
if(err)
printk("Mapping iomem region for driver '%s' failed, "
"errno = %d\n", region->driver, -err);
else {
region->virt = iomem_start;
region->phys = __pa(region->virt);
}
iomem_start += region->size + PAGE_SIZE;
region = region->next;
}
return 0;
}
__initcall(setup_iomem);

484
arch/um/kernel/process.c Normal file
View File

@@ -0,0 +1,484 @@
/*
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
* Copyright 2003 PathScale, Inc.
* Licensed under the GPL
*/
#include "linux/kernel.h"
#include "linux/sched.h"
#include "linux/interrupt.h"
#include "linux/string.h"
#include "linux/mm.h"
#include "linux/slab.h"
#include "linux/utsname.h"
#include "linux/fs.h"
#include "linux/utime.h"
#include "linux/smp_lock.h"
#include "linux/module.h"
#include "linux/init.h"
#include "linux/capability.h"
#include "linux/vmalloc.h"
#include "linux/spinlock.h"
#include "linux/proc_fs.h"
#include "linux/ptrace.h"
#include "linux/random.h"
#include "linux/personality.h"
#include "asm/unistd.h"
#include "asm/mman.h"
#include "asm/segment.h"
#include "asm/stat.h"
#include "asm/pgtable.h"
#include "asm/processor.h"
#include "asm/tlbflush.h"
#include "asm/uaccess.h"
#include "asm/user.h"
#include "user_util.h"
#include "kern_util.h"
#include "kern.h"
#include "signal_kern.h"
#include "init.h"
#include "irq_user.h"
#include "mem_user.h"
#include "tlb.h"
#include "frame_kern.h"
#include "sigcontext.h"
#include "os.h"
#include "mode.h"
#include "mode_kern.h"
#include "choose-mode.h"
#include "um_malloc.h"
/* This is a per-cpu array. A processor only modifies its entry and it only
* cares about its entry, so it's OK if another processor is modifying its
* entry.
*/
struct cpu_task cpu_tasks[NR_CPUS] = { [0 ... NR_CPUS - 1] = { -1, NULL } };
int external_pid(void *t)
{
struct task_struct *task = t ? t : current;
return(CHOOSE_MODE_PROC(external_pid_tt, external_pid_skas, task));
}
int pid_to_processor_id(int pid)
{
int i;
for(i = 0; i < ncpus; i++){
if(cpu_tasks[i].pid == pid) return(i);
}
return(-1);
}
void free_stack(unsigned long stack, int order)
{
free_pages(stack, order);
}
unsigned long alloc_stack(int order, int atomic)
{
unsigned long page;
gfp_t flags = GFP_KERNEL;
if (atomic)
flags = GFP_ATOMIC;
page = __get_free_pages(flags, order);
if(page == 0)
return(0);
stack_protections(page);
return(page);
}
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{
int pid;
current->thread.request.u.thread.proc = fn;
current->thread.request.u.thread.arg = arg;
pid = do_fork(CLONE_VM | CLONE_UNTRACED | flags, 0,
&current->thread.regs, 0, NULL, NULL);
if(pid < 0)
panic("do_fork failed in kernel_thread, errno = %d", pid);
return(pid);
}
void set_current(void *t)
{
struct task_struct *task = t;
cpu_tasks[task_thread_info(task)->cpu] = ((struct cpu_task)
{ external_pid(task), task });
}
void *_switch_to(void *prev, void *next, void *last)
{
struct task_struct *from = prev;
struct task_struct *to= next;
to->thread.prev_sched = from;
set_current(to);
do {
current->thread.saved_task = NULL ;
CHOOSE_MODE_PROC(switch_to_tt, switch_to_skas, prev, next);
if(current->thread.saved_task)
show_regs(&(current->thread.regs));
next= current->thread.saved_task;
prev= current;
} while(current->thread.saved_task);
return(current->thread.prev_sched);
}
void interrupt_end(void)
{
if(need_resched()) schedule();
if(test_tsk_thread_flag(current, TIF_SIGPENDING)) do_signal();
}
void release_thread(struct task_struct *task)
{
CHOOSE_MODE(release_thread_tt(task), release_thread_skas(task));
}
void exit_thread(void)
{
unprotect_stack((unsigned long) current_thread);
}
void *get_current(void)
{
return(current);
}
int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
unsigned long stack_top, struct task_struct * p,
struct pt_regs *regs)
{
int ret;
p->thread = (struct thread_struct) INIT_THREAD;
ret = CHOOSE_MODE_PROC(copy_thread_tt, copy_thread_skas, nr,
clone_flags, sp, stack_top, p, regs);
if (ret || !current->thread.forking)
goto out;
clear_flushed_tls(p);
/*
* Set a new TLS for the child thread?
*/
if (clone_flags & CLONE_SETTLS)
ret = arch_copy_tls(p);
out:
return ret;
}
void initial_thread_cb(void (*proc)(void *), void *arg)
{
int save_kmalloc_ok = kmalloc_ok;
kmalloc_ok = 0;
CHOOSE_MODE_PROC(initial_thread_cb_tt, initial_thread_cb_skas, proc,
arg);
kmalloc_ok = save_kmalloc_ok;
}
unsigned long stack_sp(unsigned long page)
{
return(page + PAGE_SIZE - sizeof(void *));
}
int current_pid(void)
{
return(current->pid);
}
void default_idle(void)
{
CHOOSE_MODE(uml_idle_timer(), (void) 0);
while(1){
/* endless idle loop with no priority at all */
/*
* although we are an idle CPU, we do not want to
* get into the scheduler unnecessarily.
*/
if(need_resched())
schedule();
idle_sleep(10);
}
}
void cpu_idle(void)
{
CHOOSE_MODE(init_idle_tt(), init_idle_skas());
}
int page_size(void)
{
return(PAGE_SIZE);
}
void *um_virt_to_phys(struct task_struct *task, unsigned long addr,
pte_t *pte_out)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
pte_t ptent;
if(task->mm == NULL)
return(ERR_PTR(-EINVAL));
pgd = pgd_offset(task->mm, addr);
if(!pgd_present(*pgd))
return(ERR_PTR(-EINVAL));
pud = pud_offset(pgd, addr);
if(!pud_present(*pud))
return(ERR_PTR(-EINVAL));
pmd = pmd_offset(pud, addr);
if(!pmd_present(*pmd))
return(ERR_PTR(-EINVAL));
pte = pte_offset_kernel(pmd, addr);
ptent = *pte;
if(!pte_present(ptent))
return(ERR_PTR(-EINVAL));
if(pte_out != NULL)
*pte_out = ptent;
return((void *) (pte_val(ptent) & PAGE_MASK) + (addr & ~PAGE_MASK));
}
char *current_cmd(void)
{
#if defined(CONFIG_SMP) || defined(CONFIG_HIGHMEM)
return("(Unknown)");
#else
void *addr = um_virt_to_phys(current, current->mm->arg_start, NULL);
return IS_ERR(addr) ? "(Unknown)": __va((unsigned long) addr);
#endif
}
void force_sigbus(void)
{
printk(KERN_ERR "Killing pid %d because of a lack of memory\n",
current->pid);
lock_kernel();
sigaddset(&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);
}
void *um_vmalloc_atomic(int size)
{
return __vmalloc(size, GFP_ATOMIC | __GFP_HIGHMEM, PAGE_KERNEL);
}
int __cant_sleep(void) {
return in_atomic() || irqs_disabled() || in_interrupt();
/* Is in_interrupt() really needed? */
}
unsigned long get_fault_addr(void)
{
return((unsigned long) current->thread.fault_addr);
}
EXPORT_SYMBOL(get_fault_addr);
void not_implemented(void)
{
printk(KERN_DEBUG "Something isn't implemented in here\n");
}
EXPORT_SYMBOL(not_implemented);
int user_context(unsigned long sp)
{
unsigned long stack;
stack = sp & (PAGE_MASK << CONFIG_KERNEL_STACK_ORDER);
return(stack != (unsigned long) current_thread);
}
extern exitcall_t __uml_exitcall_begin, __uml_exitcall_end;
void do_uml_exitcalls(void)
{
exitcall_t *call;
call = &__uml_exitcall_end;
while (--call >= &__uml_exitcall_begin)
(*call)();
}
char *uml_strdup(char *string)
{
return kstrdup(string, GFP_KERNEL);
}
int copy_to_user_proc(void __user *to, void *from, int size)
{
return(copy_to_user(to, from, size));
}
int copy_from_user_proc(void *to, void __user *from, int size)
{
return(copy_from_user(to, from, size));
}
int clear_user_proc(void __user *buf, int size)
{
return(clear_user(buf, size));
}
int strlen_user_proc(char __user *str)
{
return(strlen_user(str));
}
int smp_sigio_handler(void)
{
#ifdef CONFIG_SMP
int cpu = current_thread->cpu;
IPI_handler(cpu);
if(cpu != 0)
return(1);
#endif
return(0);
}
int cpu(void)
{
return(current_thread->cpu);
}
static atomic_t using_sysemu = ATOMIC_INIT(0);
int sysemu_supported;
void set_using_sysemu(int value)
{
if (value > sysemu_supported)
return;
atomic_set(&using_sysemu, value);
}
int get_using_sysemu(void)
{
return atomic_read(&using_sysemu);
}
static int proc_read_sysemu(char *buf, char **start, off_t offset, int size,int *eof, void *data)
{
if (snprintf(buf, size, "%d\n", get_using_sysemu()) < size) /*No overflow*/
*eof = 1;
return strlen(buf);
}
static int proc_write_sysemu(struct file *file,const char __user *buf, unsigned long count,void *data)
{
char tmp[2];
if (copy_from_user(tmp, buf, 1))
return -EFAULT;
if (tmp[0] >= '0' && tmp[0] <= '2')
set_using_sysemu(tmp[0] - '0');
return count; /*We use the first char, but pretend to write everything*/
}
int __init make_proc_sysemu(void)
{
struct proc_dir_entry *ent;
if (!sysemu_supported)
return 0;
ent = create_proc_entry("sysemu", 0600, &proc_root);
if (ent == NULL)
{
printk(KERN_WARNING "Failed to register /proc/sysemu\n");
return(0);
}
ent->read_proc = proc_read_sysemu;
ent->write_proc = proc_write_sysemu;
return 0;
}
late_initcall(make_proc_sysemu);
int singlestepping(void * t)
{
struct task_struct *task = t ? t : current;
if ( ! (task->ptrace & PT_DTRACE) )
return(0);
if (task->thread.singlestep_syscall)
return(1);
return 2;
}
/*
* Only x86 and x86_64 have an arch_align_stack().
* All other arches have "#define arch_align_stack(x) (x)"
* in their asm/system.h
* As this is included in UML from asm-um/system-generic.h,
* we can use it to behave as the subarch does.
*/
#ifndef arch_align_stack
unsigned long arch_align_stack(unsigned long sp)
{
if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
sp -= get_random_int() % 8192;
return sp & ~0xf;
}
#endif

321
arch/um/kernel/ptrace.c Normal file
View File

@@ -0,0 +1,321 @@
/*
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/sched.h"
#include "linux/mm.h"
#include "linux/errno.h"
#include "linux/smp_lock.h"
#include "linux/security.h"
#include "linux/ptrace.h"
#include "linux/audit.h"
#ifdef CONFIG_PROC_MM
#include "linux/proc_mm.h"
#endif
#include "asm/ptrace.h"
#include "asm/uaccess.h"
#include "kern_util.h"
#include "skas_ptrace.h"
#include "sysdep/ptrace.h"
#include "os.h"
static inline void set_singlestepping(struct task_struct *child, int on)
{
if (on)
child->ptrace |= PT_DTRACE;
else
child->ptrace &= ~PT_DTRACE;
child->thread.singlestep_syscall = 0;
#ifdef SUBARCH_SET_SINGLESTEPPING
SUBARCH_SET_SINGLESTEPPING(child, on);
#endif
}
/*
* Called by kernel/ptrace.c when detaching..
*/
void ptrace_disable(struct task_struct *child)
{
set_singlestepping(child,0);
}
extern int peek_user(struct task_struct * child, long addr, long data);
extern int poke_user(struct task_struct * child, long addr, long data);
long arch_ptrace(struct task_struct *child, long request, long addr, long data)
{
int i, ret;
unsigned long __user *p = (void __user *)(unsigned long)data;
switch (request) {
/* when I and D space are separate, these will need to be fixed. */
case PTRACE_PEEKTEXT: /* read word at location addr. */
case PTRACE_PEEKDATA: {
unsigned long tmp;
int copied;
ret = -EIO;
copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
if (copied != sizeof(tmp))
break;
ret = put_user(tmp, p);
break;
}
/* read the word at location addr in the USER area. */
case PTRACE_PEEKUSR:
ret = peek_user(child, addr, data);
break;
/* when I and D space are separate, this will have to be fixed. */
case PTRACE_POKETEXT: /* write the word at location addr. */
case PTRACE_POKEDATA:
ret = -EIO;
if (access_process_vm(child, addr, &data, sizeof(data),
1) != sizeof(data))
break;
ret = 0;
break;
case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
ret = poke_user(child, addr, data);
break;
case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
case PTRACE_CONT: { /* restart after signal. */
ret = -EIO;
if (!valid_signal(data))
break;
set_singlestepping(child, 0);
if (request == PTRACE_SYSCALL) {
set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
}
else {
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
}
child->exit_code = data;
wake_up_process(child);
ret = 0;
break;
}
/*
* make the child exit. Best I can do is send it a sigkill.
* perhaps it should be put in the status that it wants to
* exit.
*/
case PTRACE_KILL: {
ret = 0;
if (child->exit_state == EXIT_ZOMBIE) /* already dead */
break;
set_singlestepping(child, 0);
child->exit_code = SIGKILL;
wake_up_process(child);
break;
}
case PTRACE_SINGLESTEP: { /* set the trap flag. */
ret = -EIO;
if (!valid_signal(data))
break;
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
set_singlestepping(child, 1);
child->exit_code = data;
/* give it a chance to run. */
wake_up_process(child);
ret = 0;
break;
}
case PTRACE_DETACH:
/* detach a process that was attached. */
ret = ptrace_detach(child, data);
break;
#ifdef PTRACE_GETREGS
case PTRACE_GETREGS: { /* Get all gp regs from the child. */
if (!access_ok(VERIFY_WRITE, p, MAX_REG_OFFSET)) {
ret = -EIO;
break;
}
for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) {
__put_user(getreg(child, i), p);
p++;
}
ret = 0;
break;
}
#endif
#ifdef PTRACE_SETREGS
case PTRACE_SETREGS: { /* Set all gp regs in the child. */
unsigned long tmp = 0;
if (!access_ok(VERIFY_READ, p, MAX_REG_OFFSET)) {
ret = -EIO;
break;
}
for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) {
__get_user(tmp, p);
putreg(child, i, tmp);
p++;
}
ret = 0;
break;
}
#endif
#ifdef PTRACE_GETFPREGS
case PTRACE_GETFPREGS: /* Get the child FPU state. */
ret = get_fpregs(data, child);
break;
#endif
#ifdef PTRACE_SETFPREGS
case PTRACE_SETFPREGS: /* Set the child FPU state. */
ret = set_fpregs(data, child);
break;
#endif
#ifdef PTRACE_GETFPXREGS
case PTRACE_GETFPXREGS: /* Get the child FPU state. */
ret = get_fpxregs(data, child);
break;
#endif
#ifdef PTRACE_SETFPXREGS
case PTRACE_SETFPXREGS: /* Set the child FPU state. */
ret = set_fpxregs(data, child);
break;
#endif
case PTRACE_GET_THREAD_AREA:
ret = ptrace_get_thread_area(child, addr,
(struct user_desc __user *) data);
break;
case PTRACE_SET_THREAD_AREA:
ret = ptrace_set_thread_area(child, addr,
(struct user_desc __user *) data);
break;
case PTRACE_FAULTINFO: {
/* Take the info from thread->arch->faultinfo,
* but transfer max. sizeof(struct ptrace_faultinfo).
* On i386, ptrace_faultinfo is smaller!
*/
ret = copy_to_user(p, &child->thread.arch.faultinfo,
sizeof(struct ptrace_faultinfo));
if(ret)
break;
break;
}
#ifdef PTRACE_LDT
case PTRACE_LDT: {
struct ptrace_ldt ldt;
if(copy_from_user(&ldt, p, sizeof(ldt))){
ret = -EIO;
break;
}
/* This one is confusing, so just punt and return -EIO for
* now
*/
ret = -EIO;
break;
}
#endif
#ifdef CONFIG_PROC_MM
case PTRACE_SWITCH_MM: {
struct mm_struct *old = child->mm;
struct mm_struct *new = proc_mm_get_mm(data);
if(IS_ERR(new)){
ret = PTR_ERR(new);
break;
}
atomic_inc(&new->mm_users);
child->mm = new;
child->active_mm = new;
mmput(old);
ret = 0;
break;
}
#endif
#ifdef PTRACE_ARCH_PRCTL
case PTRACE_ARCH_PRCTL:
/* XXX Calls ptrace on the host - needs some SMP thinking */
ret = arch_prctl_skas(child, data, (void *) addr);
break;
#endif
default:
ret = ptrace_request(child, request, addr, data);
break;
}
return ret;
}
void send_sigtrap(struct task_struct *tsk, union uml_pt_regs *regs,
int error_code)
{
struct siginfo info;
memset(&info, 0, sizeof(info));
info.si_signo = SIGTRAP;
info.si_code = TRAP_BRKPT;
/* User-mode eip? */
info.si_addr = UPT_IS_USER(regs) ? (void __user *) UPT_IP(regs) : NULL;
/* Send us the fakey SIGTRAP */
force_sig_info(SIGTRAP, &info, tsk);
}
/* XXX Check PT_DTRACE vs TIF_SINGLESTEP for singlestepping check and
* PT_PTRACED vs TIF_SYSCALL_TRACE for syscall tracing check
*/
void syscall_trace(union uml_pt_regs *regs, int entryexit)
{
int is_singlestep = (current->ptrace & PT_DTRACE) && entryexit;
int tracesysgood;
if (unlikely(current->audit_context)) {
if (!entryexit)
audit_syscall_entry(HOST_AUDIT_ARCH,
UPT_SYSCALL_NR(regs),
UPT_SYSCALL_ARG1(regs),
UPT_SYSCALL_ARG2(regs),
UPT_SYSCALL_ARG3(regs),
UPT_SYSCALL_ARG4(regs));
else audit_syscall_exit(AUDITSC_RESULT(UPT_SYSCALL_RET(regs)),
UPT_SYSCALL_RET(regs));
}
/* Fake a debug trap */
if (is_singlestep)
send_sigtrap(current, regs, 0);
if (!test_thread_flag(TIF_SYSCALL_TRACE))
return;
if (!(current->ptrace & PT_PTRACED))
return;
/* the 0x80 provides a way for the tracing parent to distinguish
between a syscall stop and SIGTRAP delivery */
tracesysgood = (current->ptrace & PT_TRACESYSGOOD);
ptrace_notify(SIGTRAP | (tracesysgood ? 0x80 : 0));
if (entryexit) /* force do_signal() --> is_syscall() */
set_thread_flag(TIF_SIGPENDING);
/* this isn't the same as continuing with a signal, but it will do
* for normal use. strace only continues with a signal if the
* stopping signal is not SIGTRAP. -brl
*/
if (current->exit_code) {
send_sig(current->exit_code, current, 1);
current->exit_code = 0;
}
}

64
arch/um/kernel/reboot.c Normal file
View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2000, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/module.h"
#include "linux/sched.h"
#include "asm/smp.h"
#include "user_util.h"
#include "kern_util.h"
#include "kern.h"
#include "os.h"
#include "mode.h"
#include "choose-mode.h"
void (*pm_power_off)(void);
#ifdef CONFIG_SMP
static void kill_idlers(int me)
{
#ifdef CONFIG_MODE_TT
struct task_struct *p;
int i;
for(i = 0; i < ARRAY_SIZE(idle_threads); i++){
p = idle_threads[i];
if((p != NULL) && (p->thread.mode.tt.extern_pid != me))
os_kill_process(p->thread.mode.tt.extern_pid, 0);
}
#endif
}
#endif
static void kill_off_processes(void)
{
CHOOSE_MODE(kill_off_processes_tt(), kill_off_processes_skas());
#ifdef CONFIG_SMP
kill_idlers(os_getpid());
#endif
}
void uml_cleanup(void)
{
kmalloc_ok = 0;
do_uml_exitcalls();
kill_off_processes();
}
void machine_restart(char * __unused)
{
uml_cleanup();
CHOOSE_MODE(reboot_tt(), reboot_skas());
}
void machine_power_off(void)
{
uml_cleanup();
CHOOSE_MODE(halt_tt(), halt_skas());
}
void machine_halt(void)
{
machine_power_off();
}

56
arch/um/kernel/sigio.c Normal file
View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com)
* Licensed under the GPL
*/
#include "linux/kernel.h"
#include "linux/list.h"
#include "linux/slab.h"
#include "linux/signal.h"
#include "linux/interrupt.h"
#include "init.h"
#include "sigio.h"
#include "irq_user.h"
#include "irq_kern.h"
#include "os.h"
/* Protected by sigio_lock() called from write_sigio_workaround */
static int sigio_irq_fd = -1;
static irqreturn_t sigio_interrupt(int irq, void *data)
{
char c;
os_read_file(sigio_irq_fd, &c, sizeof(c));
reactivate_fd(sigio_irq_fd, SIGIO_WRITE_IRQ);
return IRQ_HANDLED;
}
int write_sigio_irq(int fd)
{
int err;
err = um_request_irq(SIGIO_WRITE_IRQ, fd, IRQ_READ, sigio_interrupt,
IRQF_DISABLED|IRQF_SAMPLE_RANDOM, "write sigio",
NULL);
if(err){
printk("write_sigio_irq : um_request_irq failed, err = %d\n",
err);
return -1;
}
sigio_irq_fd = fd;
return 0;
}
/* These are called from os-Linux/sigio.c to protect its pollfds arrays. */
static DEFINE_SPINLOCK(sigio_spinlock);
void sigio_lock(void)
{
spin_lock(&sigio_spinlock);
}
void sigio_unlock(void)
{
spin_unlock(&sigio_spinlock);
}

190
arch/um/kernel/signal.c Normal file
View File

@@ -0,0 +1,190 @@
/*
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/stddef.h"
#include "linux/sys.h"
#include "linux/sched.h"
#include "linux/wait.h"
#include "linux/kernel.h"
#include "linux/smp_lock.h"
#include "linux/module.h"
#include "linux/slab.h"
#include "linux/tty.h"
#include "linux/binfmts.h"
#include "linux/ptrace.h"
#include "asm/signal.h"
#include "asm/uaccess.h"
#include "asm/unistd.h"
#include "user_util.h"
#include "asm/ucontext.h"
#include "kern_util.h"
#include "signal_kern.h"
#include "kern.h"
#include "frame_kern.h"
#include "sigcontext.h"
#include "mode.h"
EXPORT_SYMBOL(block_signals);
EXPORT_SYMBOL(unblock_signals);
#define _S(nr) (1<<((nr)-1))
#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
/*
* OK, we're invoking a handler
*/
static int handle_signal(struct pt_regs *regs, unsigned long signr,
struct k_sigaction *ka, siginfo_t *info,
sigset_t *oldset)
{
unsigned long sp;
int err;
/* Always make any pending restarted system calls return -EINTR */
current_thread_info()->restart_block.fn = do_no_restart_syscall;
/* Did we come from a system call? */
if(PT_REGS_SYSCALL_NR(regs) >= 0){
/* If so, check system call restarting.. */
switch(PT_REGS_SYSCALL_RET(regs)){
case -ERESTART_RESTARTBLOCK:
case -ERESTARTNOHAND:
PT_REGS_SYSCALL_RET(regs) = -EINTR;
break;
case -ERESTARTSYS:
if (!(ka->sa.sa_flags & SA_RESTART)) {
PT_REGS_SYSCALL_RET(regs) = -EINTR;
break;
}
/* fallthrough */
case -ERESTARTNOINTR:
PT_REGS_RESTART_SYSCALL(regs);
PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs);
break;
}
}
sp = PT_REGS_SP(regs);
if((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags(sp) == 0))
sp = current->sas_ss_sp + current->sas_ss_size;
#ifdef CONFIG_ARCH_HAS_SC_SIGNALS
if(!(ka->sa.sa_flags & SA_SIGINFO))
err = setup_signal_stack_sc(sp, signr, ka, regs, oldset);
else
#endif
err = setup_signal_stack_si(sp, signr, ka, regs, info, oldset);
if(err){
spin_lock_irq(&current->sighand->siglock);
current->blocked = *oldset;
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
force_sigsegv(signr, current);
} else {
spin_lock_irq(&current->sighand->siglock);
sigorsets(&current->blocked, &current->blocked,
&ka->sa.sa_mask);
if(!(ka->sa.sa_flags & SA_NODEFER))
sigaddset(&current->blocked, signr);
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
}
return err;
}
static int kern_do_signal(struct pt_regs *regs)
{
struct k_sigaction ka_copy;
siginfo_t info;
sigset_t *oldset;
int sig, handled_sig = 0;
if (test_thread_flag(TIF_RESTORE_SIGMASK))
oldset = &current->saved_sigmask;
else
oldset = &current->blocked;
while((sig = get_signal_to_deliver(&info, &ka_copy, regs, NULL)) > 0){
handled_sig = 1;
/* Whee! Actually deliver the signal. */
if(!handle_signal(regs, sig, &ka_copy, &info, oldset)){
/* a signal was successfully delivered; the saved
* sigmask will have been stored in the signal frame,
* and will be restored by sigreturn, so we can simply
* clear the TIF_RESTORE_SIGMASK flag */
if (test_thread_flag(TIF_RESTORE_SIGMASK))
clear_thread_flag(TIF_RESTORE_SIGMASK);
break;
}
}
/* Did we come from a system call? */
if(!handled_sig && (PT_REGS_SYSCALL_NR(regs) >= 0)){
/* Restart the system call - no handlers present */
switch(PT_REGS_SYSCALL_RET(regs)){
case -ERESTARTNOHAND:
case -ERESTARTSYS:
case -ERESTARTNOINTR:
PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs);
PT_REGS_RESTART_SYSCALL(regs);
break;
case -ERESTART_RESTARTBLOCK:
PT_REGS_ORIG_SYSCALL(regs) = __NR_restart_syscall;
PT_REGS_RESTART_SYSCALL(regs);
break;
}
}
/* This closes a way to execute a system call on the host. If
* you set a breakpoint on a system call instruction and singlestep
* from it, the tracing thread used to PTRACE_SINGLESTEP the process
* rather than PTRACE_SYSCALL it, allowing the system call to execute
* on the host. The tracing thread will check this flag and
* PTRACE_SYSCALL if necessary.
*/
if(current->ptrace & PT_DTRACE)
current->thread.singlestep_syscall =
is_syscall(PT_REGS_IP(&current->thread.regs));
/* if there's no signal to deliver, we just put the saved sigmask
* back */
if (!handled_sig && test_thread_flag(TIF_RESTORE_SIGMASK)) {
clear_thread_flag(TIF_RESTORE_SIGMASK);
sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
}
return handled_sig;
}
int do_signal(void)
{
return kern_do_signal(&current->thread.regs);
}
/*
* Atomically swap in the new signal mask, and wait for a signal.
*/
long sys_sigsuspend(int history0, int history1, old_sigset_t mask)
{
mask &= _BLOCKABLE;
spin_lock_irq(&current->sighand->siglock);
current->saved_sigmask = current->blocked;
siginitset(&current->blocked, mask);
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
current->state = TASK_INTERRUPTIBLE;
schedule();
set_thread_flag(TIF_RESTORE_SIGMASK);
return -ERESTARTNOHAND;
}
long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss)
{
return do_sigaltstack(uss, uoss, PT_REGS_SP(&current->thread.regs));
}

View File

@@ -0,0 +1,15 @@
#
# Copyright (C) 2002 - 2004 Jeff Dike (jdike@addtoit.com)
# Licensed under the GPL
#
obj-y := clone.o exec.o mem.o mmu.o process.o syscall.o tlb.o uaccess.o
# clone.o is in the stub, so it can't be built with profiling
# GCC hardened also auto-enables -fpic, but we need %ebx so it can't work ->
# disable it
CFLAGS_clone.o := $(CFLAGS_NO_HARDENING)
UNPROFILE_OBJS := clone.o
include arch/um/scripts/Makefile.rules

View File

@@ -0,0 +1,54 @@
#include <sched.h>
#include <signal.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <asm/unistd.h>
#include <asm/page.h>
#include "ptrace_user.h"
#include "skas.h"
#include "stub-data.h"
#include "uml-config.h"
#include "sysdep/stub.h"
#include "kern_constants.h"
/* This is in a separate file because it needs to be compiled with any
* extraneous gcc flags (-pg, -fprofile-arcs, -ftest-coverage) disabled
*
* Use UM_KERN_PAGE_SIZE instead of PAGE_SIZE because that calls getpagesize
* on some systems.
*/
void __attribute__ ((__section__ (".__syscall_stub")))
stub_clone_handler(void)
{
struct stub_data *data = (struct stub_data *) UML_CONFIG_STUB_DATA;
long err;
err = stub_syscall2(__NR_clone, CLONE_PARENT | CLONE_FILES | SIGCHLD,
UML_CONFIG_STUB_DATA + UM_KERN_PAGE_SIZE / 2 -
sizeof(void *));
if(err != 0)
goto out;
err = stub_syscall4(__NR_ptrace, PTRACE_TRACEME, 0, 0, 0);
if(err)
goto out;
err = stub_syscall3(__NR_setitimer, ITIMER_VIRTUAL,
(long) &data->timer, 0);
if(err)
goto out;
remap_stack(data->fd, data->offset);
goto done;
out:
/* save current result.
* Parent: pid;
* child: retcode of mmap already saved and it jumps around this
* assignment
*/
data->err = err;
done:
trap_myself();
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/kernel.h"
#include "asm/current.h"
#include "asm/page.h"
#include "asm/signal.h"
#include "asm/ptrace.h"
#include "asm/uaccess.h"
#include "asm/mmu_context.h"
#include "tlb.h"
#include "skas.h"
#include "um_mmu.h"
#include "os.h"
void flush_thread_skas(void)
{
force_flush_all();
switch_mm_skas(&current->mm->context.skas.id);
}
void start_thread_skas(struct pt_regs *regs, unsigned long eip,
unsigned long esp)
{
set_fs(USER_DS);
PT_REGS_IP(regs) = eip;
PT_REGS_SP(regs) = esp;
}

22
arch/um/kernel/skas/mem.c Normal file
View File

@@ -0,0 +1,22 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/mm.h"
#include "asm/pgtable.h"
#include "mem_user.h"
#include "skas.h"
unsigned long set_task_sizes_skas(unsigned long *task_size_out)
{
/* Round up to the nearest 4M */
unsigned long host_task_size = ROUND_4M((unsigned long)
&host_task_size);
if (!skas_needs_stub)
*task_size_out = host_task_size;
else *task_size_out = CONFIG_STUB_START & PGDIR_MASK;
return host_task_size;
}

158
arch/um/kernel/skas/mmu.c Normal file
View File

@@ -0,0 +1,158 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/sched.h"
#include "linux/list.h"
#include "linux/spinlock.h"
#include "linux/slab.h"
#include "linux/errno.h"
#include "linux/mm.h"
#include "asm/current.h"
#include "asm/segment.h"
#include "asm/mmu.h"
#include "asm/pgalloc.h"
#include "asm/pgtable.h"
#include "asm/ldt.h"
#include "os.h"
#include "skas.h"
extern int __syscall_stub_start;
static int init_stub_pte(struct mm_struct *mm, unsigned long proc,
unsigned long kernel)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
pgd = pgd_offset(mm, proc);
pud = pud_alloc(mm, pgd, proc);
if (!pud)
goto out;
pmd = pmd_alloc(mm, pud, proc);
if (!pmd)
goto out_pmd;
pte = pte_alloc_map(mm, pmd, proc);
if (!pte)
goto out_pte;
/* There's an interaction between the skas0 stub pages, stack
* randomization, and the BUG at the end of exit_mmap. exit_mmap
* checks that the number of page tables freed is the same as had
* been allocated. If the stack is on the last page table page,
* then the stack pte page will be freed, and if not, it won't. To
* avoid having to know where the stack is, or if the process mapped
* something at the top of its address space for some other reason,
* we set TASK_SIZE to end at the start of the last page table.
* This keeps exit_mmap off the last page, but introduces a leak
* of that page. So, we hang onto it here and free it in
* destroy_context_skas.
*/
mm->context.skas.last_page_table = pmd_page_vaddr(*pmd);
#ifdef CONFIG_3_LEVEL_PGTABLES
mm->context.skas.last_pmd = (unsigned long) __va(pud_val(*pud));
#endif
*pte = mk_pte(virt_to_page(kernel), __pgprot(_PAGE_PRESENT));
*pte = pte_mkread(*pte);
return(0);
out_pmd:
pud_free(pud);
out_pte:
pmd_free(pmd);
out:
return(-ENOMEM);
}
int init_new_context_skas(struct task_struct *task, struct mm_struct *mm)
{
struct mmu_context_skas *from_mm = NULL;
struct mmu_context_skas *to_mm = &mm->context.skas;
unsigned long stack = 0;
int ret = -ENOMEM;
if(skas_needs_stub){
stack = get_zeroed_page(GFP_KERNEL);
if(stack == 0)
goto out;
/* This zeros the entry that pgd_alloc didn't, needed since
* we are about to reinitialize it, and want mm.nr_ptes to
* be accurate.
*/
mm->pgd[USER_PTRS_PER_PGD] = __pgd(0);
ret = init_stub_pte(mm, CONFIG_STUB_CODE,
(unsigned long) &__syscall_stub_start);
if(ret)
goto out_free;
ret = init_stub_pte(mm, CONFIG_STUB_DATA, stack);
if(ret)
goto out_free;
mm->nr_ptes--;
}
to_mm->id.stack = stack;
if(current->mm != NULL && current->mm != &init_mm)
from_mm = &current->mm->context.skas;
if(proc_mm){
ret = new_mm(stack);
if(ret < 0){
printk("init_new_context_skas - new_mm failed, "
"errno = %d\n", ret);
goto out_free;
}
to_mm->id.u.mm_fd = ret;
}
else {
if(from_mm)
to_mm->id.u.pid = copy_context_skas0(stack,
from_mm->id.u.pid);
else to_mm->id.u.pid = start_userspace(stack);
}
ret = init_new_ldt(to_mm, from_mm);
if(ret < 0){
printk("init_new_context_skas - init_ldt"
" failed, errno = %d\n", ret);
goto out_free;
}
return 0;
out_free:
if(to_mm->id.stack != 0)
free_page(to_mm->id.stack);
out:
return ret;
}
void destroy_context_skas(struct mm_struct *mm)
{
struct mmu_context_skas *mmu = &mm->context.skas;
if(proc_mm)
os_close_file(mmu->id.u.mm_fd);
else
os_kill_ptraced_process(mmu->id.u.pid, 1);
if(!proc_mm || !ptrace_faultinfo){
free_page(mmu->id.stack);
pte_lock_deinit(virt_to_page(mmu->last_page_table));
pte_free_kernel((pte_t *) mmu->last_page_table);
dec_zone_page_state(virt_to_page(mmu->last_page_table), NR_PAGETABLE);
#ifdef CONFIG_3_LEVEL_PGTABLES
pmd_free((pmd_t *) mmu->last_pmd);
#endif
}
}

View File

@@ -0,0 +1,217 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/sched.h"
#include "linux/slab.h"
#include "linux/ptrace.h"
#include "linux/proc_fs.h"
#include "linux/file.h"
#include "linux/errno.h"
#include "linux/init.h"
#include "asm/uaccess.h"
#include "asm/atomic.h"
#include "kern_util.h"
#include "skas.h"
#include "os.h"
#include "user_util.h"
#include "tlb.h"
#include "kern.h"
#include "mode.h"
#include "registers.h"
void switch_to_skas(void *prev, void *next)
{
struct task_struct *from, *to;
from = prev;
to = next;
/* XXX need to check runqueues[cpu].idle */
if(current->pid == 0)
switch_timers(0);
switch_threads(&from->thread.mode.skas.switch_buf,
&to->thread.mode.skas.switch_buf);
arch_switch_to_skas(current->thread.prev_sched, current);
if(current->pid == 0)
switch_timers(1);
}
extern void schedule_tail(struct task_struct *prev);
/* This is called magically, by its address being stuffed in a jmp_buf
* and being longjmp-d to.
*/
void new_thread_handler(void)
{
int (*fn)(void *), n;
void *arg;
if(current->thread.prev_sched != NULL)
schedule_tail(current->thread.prev_sched);
current->thread.prev_sched = NULL;
fn = current->thread.request.u.thread.proc;
arg = current->thread.request.u.thread.arg;
/* The return value is 1 if the kernel thread execs a process,
* 0 if it just exits
*/
n = run_kernel_thread(fn, arg, &current->thread.exec_buf);
if(n == 1){
/* Handle any immediate reschedules or signals */
interrupt_end();
userspace(&current->thread.regs.regs);
}
else do_exit(0);
}
void release_thread_skas(struct task_struct *task)
{
}
/* Called magically, see new_thread_handler above */
void fork_handler(void)
{
force_flush_all();
if(current->thread.prev_sched == NULL)
panic("blech");
schedule_tail(current->thread.prev_sched);
/* XXX: if interrupt_end() calls schedule, this call to
* arch_switch_to_skas isn't needed. We could want to apply this to
* improve performance. -bb */
arch_switch_to_skas(current->thread.prev_sched, current);
current->thread.prev_sched = NULL;
/* Handle any immediate reschedules or signals */
interrupt_end();
userspace(&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)(void);
if(current->thread.forking){
memcpy(&p->thread.regs.regs.skas, &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;
arch_copy_thread(&current->thread.arch, &p->thread.arch);
}
else {
init_thread_registers(&p->thread.regs.regs);
p->thread.request.u.thread = current->thread.request.u.thread;
handler = new_thread_handler;
}
new_thread(task_stack_page(p), &p->thread.mode.skas.switch_buf,
handler);
return(0);
}
int new_mm(unsigned long stack)
{
int fd;
fd = os_open_file("/proc/mm", of_cloexec(of_write(OPENFLAGS())), 0);
if(fd < 0)
return(fd);
if(skas_needs_stub)
map_stub_pages(fd, CONFIG_STUB_CODE, CONFIG_STUB_DATA, stack);
return(fd);
}
void init_idle_skas(void)
{
cpu_tasks[current_thread->cpu].pid = os_getpid();
default_idle();
}
extern void start_kernel(void);
static int start_kernel_proc(void *unused)
{
int pid;
block_signals();
pid = os_getpid();
cpu_tasks[0].pid = pid;
cpu_tasks[0].task = current;
#ifdef CONFIG_SMP
cpu_online_map = cpumask_of_cpu(0);
#endif
start_kernel();
return(0);
}
extern int userspace_pid[];
int start_uml_skas(void)
{
if(proc_mm)
userspace_pid[0] = start_userspace(0);
init_new_thread_signals();
init_task.thread.request.u.thread.proc = start_kernel_proc;
init_task.thread.request.u.thread.arg = NULL;
return(start_idle_thread(task_stack_page(&init_task),
&init_task.thread.mode.skas.switch_buf));
}
int external_pid_skas(struct task_struct *task)
{
#warning Need to look up userspace_pid by cpu
return(userspace_pid[0]);
}
int thread_pid_skas(struct task_struct *task)
{
#warning Need to look up userspace_pid by cpu
return(userspace_pid[0]);
}
void kill_off_processes_skas(void)
{
if(proc_mm)
#warning need to loop over userspace_pids in kill_off_processes_skas
os_kill_ptraced_process(userspace_pid[0], 1);
else {
struct task_struct *p;
int pid, me;
me = os_getpid();
for_each_process(p){
if(p->mm == NULL)
continue;
pid = p->mm->context.skas.id.u.pid;
os_kill_ptraced_process(pid, 1);
}
}
}
unsigned long current_stub_stack(void)
{
if(current->mm == NULL)
return(0);
return(current->mm->context.skas.id.stack);
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/sys.h"
#include "linux/ptrace.h"
#include "asm/errno.h"
#include "asm/unistd.h"
#include "asm/ptrace.h"
#include "asm/current.h"
#include "sysdep/syscalls.h"
#include "kern_util.h"
#include "syscall.h"
void handle_syscall(union uml_pt_regs *r)
{
struct pt_regs *regs = container_of(r, struct pt_regs, regs);
long result;
int syscall;
syscall_trace(r, 0);
current->thread.nsyscalls++;
nsyscalls++;
/* This should go in the declaration of syscall, but when I do that,
* strace -f -c bash -c 'ls ; ls' breaks, sometimes not tracing
* children at all, sometimes hanging when bash doesn't see the first
* ls exit.
* The assembly looks functionally the same to me. This is
* gcc version 4.0.1 20050727 (Red Hat 4.0.1-5)
* in case it's a compiler bug.
*/
syscall = UPT_SYSCALL_NR(r);
if((syscall >= NR_syscalls) || (syscall < 0))
result = -ENOSYS;
else result = EXECUTE_SYSCALL(syscall, regs);
REGS_SET_SYSCALL_RETURN(r->skas.regs, result);
syscall_trace(r, 1);
}

97
arch/um/kernel/skas/tlb.c Normal file
View File

@@ -0,0 +1,97 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Copyright 2003 PathScale, Inc.
* Licensed under the GPL
*/
#include "linux/stddef.h"
#include "linux/sched.h"
#include "linux/mm.h"
#include "asm/page.h"
#include "asm/pgtable.h"
#include "asm/mmu.h"
#include "user_util.h"
#include "mem_user.h"
#include "mem.h"
#include "skas.h"
#include "os.h"
#include "tlb.h"
static int do_ops(union mm_context *mmu, struct host_vm_op *ops, int last,
int finished, void **flush)
{
struct host_vm_op *op;
int i, ret = 0;
for(i = 0; i <= last && !ret; i++){
op = &ops[i];
switch(op->type){
case MMAP:
ret = map(&mmu->skas.id, op->u.mmap.addr,
op->u.mmap.len, op->u.mmap.r, op->u.mmap.w,
op->u.mmap.x, op->u.mmap.fd,
op->u.mmap.offset, finished, flush);
break;
case MUNMAP:
ret = unmap(&mmu->skas.id,
(void *) op->u.munmap.addr,
op->u.munmap.len, finished, flush);
break;
case MPROTECT:
ret = protect(&mmu->skas.id, op->u.mprotect.addr,
op->u.mprotect.len, op->u.mprotect.r,
op->u.mprotect.w, op->u.mprotect.x,
finished, flush);
break;
default:
printk("Unknown op type %d in do_ops\n", op->type);
break;
}
}
return ret;
}
extern int proc_mm;
static void fix_range(struct mm_struct *mm, unsigned long start_addr,
unsigned long end_addr, int force)
{
if(!proc_mm && (end_addr > CONFIG_STUB_START))
end_addr = CONFIG_STUB_START;
fix_range_common(mm, start_addr, end_addr, force, do_ops);
}
void __flush_tlb_one_skas(unsigned long addr)
{
flush_tlb_kernel_range_common(addr, addr + PAGE_SIZE);
}
void flush_tlb_range_skas(struct vm_area_struct *vma, unsigned long start,
unsigned long end)
{
if(vma->vm_mm == NULL)
flush_tlb_kernel_range_common(start, end);
else fix_range(vma->vm_mm, start, end, 0);
}
void flush_tlb_mm_skas(struct mm_struct *mm)
{
unsigned long end;
/* Don't bother flushing if this address space is about to be
* destroyed.
*/
if(atomic_read(&mm->mm_users) == 0)
return;
end = proc_mm ? task_size : CONFIG_STUB_START;
fix_range(mm, 0, end, 0);
}
void force_flush_all_skas(void)
{
unsigned long end = proc_mm ? task_size : CONFIG_STUB_START;
fix_range(current->mm, 0, end, 1);
}

View File

@@ -0,0 +1,265 @@
/*
* Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com)
* Licensed under the GPL
*/
#include "linux/compiler.h"
#include "linux/stddef.h"
#include "linux/kernel.h"
#include "linux/string.h"
#include "linux/fs.h"
#include "linux/hardirq.h"
#include "linux/highmem.h"
#include "asm/page.h"
#include "asm/pgtable.h"
#include "asm/uaccess.h"
#include "kern_util.h"
#include "os.h"
extern void *um_virt_to_phys(struct task_struct *task, unsigned long addr,
pte_t *pte_out);
static unsigned long maybe_map(unsigned long virt, int is_write)
{
pte_t pte;
int err;
void *phys = um_virt_to_phys(current, virt, &pte);
int dummy_code;
if(IS_ERR(phys) || (is_write && !pte_write(pte))){
err = handle_page_fault(virt, 0, is_write, 1, &dummy_code);
if(err)
return(-1UL);
phys = um_virt_to_phys(current, virt, NULL);
}
if(IS_ERR(phys))
phys = (void *) -1;
return((unsigned long) phys);
}
static int do_op_one_page(unsigned long addr, int len, int is_write,
int (*op)(unsigned long addr, int len, void *arg), void *arg)
{
struct page *page;
int n;
addr = maybe_map(addr, is_write);
if(addr == -1UL)
return(-1);
page = phys_to_page(addr);
addr = (unsigned long) kmap_atomic(page, KM_UML_USERCOPY) + (addr & ~PAGE_MASK);
n = (*op)(addr, len, arg);
kunmap_atomic(page, KM_UML_USERCOPY);
return(n);
}
static void do_buffer_op(void *jmpbuf, void *arg_ptr)
{
va_list args;
unsigned long addr;
int len, is_write, size, remain, n;
int (*op)(unsigned long, int, void *);
void *arg;
int *res;
va_copy(args, *(va_list *)arg_ptr);
addr = va_arg(args, unsigned long);
len = va_arg(args, int);
is_write = va_arg(args, int);
op = va_arg(args, void *);
arg = va_arg(args, void *);
res = va_arg(args, int *);
va_end(args);
size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len);
remain = len;
current->thread.fault_catcher = jmpbuf;
n = do_op_one_page(addr, size, is_write, op, arg);
if(n != 0){
*res = (n < 0 ? remain : 0);
goto out;
}
addr += size;
remain -= size;
if(remain == 0){
*res = 0;
goto out;
}
while(addr < ((addr + remain) & PAGE_MASK)){
n = do_op_one_page(addr, PAGE_SIZE, is_write, op, arg);
if(n != 0){
*res = (n < 0 ? remain : 0);
goto out;
}
addr += PAGE_SIZE;
remain -= PAGE_SIZE;
}
if(remain == 0){
*res = 0;
goto out;
}
n = do_op_one_page(addr, remain, is_write, op, arg);
if(n != 0)
*res = (n < 0 ? remain : 0);
else *res = 0;
out:
current->thread.fault_catcher = NULL;
}
static int buffer_op(unsigned long addr, int len, int is_write,
int (*op)(unsigned long addr, int len, void *arg),
void *arg)
{
int faulted, res;
faulted = setjmp_wrapper(do_buffer_op, addr, len, is_write, op, arg,
&res);
if(!faulted)
return(res);
return(addr + len - (unsigned long) current->thread.fault_addr);
}
static int copy_chunk_from_user(unsigned long from, int len, void *arg)
{
unsigned long *to_ptr = arg, to = *to_ptr;
memcpy((void *) to, (void *) from, len);
*to_ptr += len;
return(0);
}
int copy_from_user_skas(void *to, const void __user *from, int n)
{
if(segment_eq(get_fs(), KERNEL_DS)){
memcpy(to, (__force void*)from, n);
return(0);
}
return(access_ok(VERIFY_READ, from, n) ?
buffer_op((unsigned long) from, n, 0, copy_chunk_from_user, &to):
n);
}
static int copy_chunk_to_user(unsigned long to, int len, void *arg)
{
unsigned long *from_ptr = arg, from = *from_ptr;
memcpy((void *) to, (void *) from, len);
*from_ptr += len;
return(0);
}
int copy_to_user_skas(void __user *to, const void *from, int n)
{
if(segment_eq(get_fs(), KERNEL_DS)){
memcpy((__force void*)to, from, n);
return(0);
}
return(access_ok(VERIFY_WRITE, to, n) ?
buffer_op((unsigned long) to, n, 1, copy_chunk_to_user, &from) :
n);
}
static int strncpy_chunk_from_user(unsigned long from, int len, void *arg)
{
char **to_ptr = arg, *to = *to_ptr;
int n;
strncpy(to, (void *) from, len);
n = strnlen(to, len);
*to_ptr += n;
if(n < len)
return(1);
return(0);
}
int strncpy_from_user_skas(char *dst, const char __user *src, int count)
{
int n;
char *ptr = dst;
if(segment_eq(get_fs(), KERNEL_DS)){
strncpy(dst, (__force void*)src, count);
return(strnlen(dst, count));
}
if(!access_ok(VERIFY_READ, src, 1))
return(-EFAULT);
n = buffer_op((unsigned long) src, count, 0, strncpy_chunk_from_user,
&ptr);
if(n != 0)
return(-EFAULT);
return(strnlen(dst, count));
}
static int clear_chunk(unsigned long addr, int len, void *unused)
{
memset((void *) addr, 0, len);
return(0);
}
int __clear_user_skas(void __user *mem, int len)
{
return(buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL));
}
int clear_user_skas(void __user *mem, int len)
{
if(segment_eq(get_fs(), KERNEL_DS)){
memset((__force void*)mem, 0, len);
return(0);
}
return(access_ok(VERIFY_WRITE, mem, len) ?
buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL) : len);
}
static int strnlen_chunk(unsigned long str, int len, void *arg)
{
int *len_ptr = arg, n;
n = strnlen((void *) str, len);
*len_ptr += n;
if(n < len)
return(1);
return(0);
}
int strnlen_user_skas(const void __user *str, int len)
{
int count = 0, n;
if(segment_eq(get_fs(), KERNEL_DS))
return(strnlen((__force char*)str, len) + 1);
n = buffer_op((unsigned long) str, len, 0, strnlen_chunk, &count);
if(n == 0)
return(count + 1);
return(-EFAULT);
}
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

267
arch/um/kernel/smp.c Normal file
View File

@@ -0,0 +1,267 @@
/*
* Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
* Licensed under the GPL
*/
#include "linux/percpu.h"
#include "asm/pgalloc.h"
#include "asm/tlb.h"
/* For some reason, mmu_gathers are referenced when CONFIG_SMP is off. */
DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
#ifdef CONFIG_SMP
#include "linux/sched.h"
#include "linux/module.h"
#include "linux/threads.h"
#include "linux/interrupt.h"
#include "linux/err.h"
#include "linux/hardirq.h"
#include "asm/smp.h"
#include "asm/processor.h"
#include "asm/spinlock.h"
#include "user_util.h"
#include "kern_util.h"
#include "kern.h"
#include "irq_user.h"
#include "os.h"
/* CPU online map, set by smp_boot_cpus */
cpumask_t cpu_online_map = CPU_MASK_NONE;
cpumask_t cpu_possible_map = CPU_MASK_NONE;
EXPORT_SYMBOL(cpu_online_map);
EXPORT_SYMBOL(cpu_possible_map);
/* Per CPU bogomips and other parameters
* The only piece used here is the ipi pipe, which is set before SMP is
* started and never changed.
*/
struct cpuinfo_um cpu_data[NR_CPUS];
/* A statistic, can be a little off */
int num_reschedules_sent = 0;
/* Not changed after boot */
struct task_struct *idle_threads[NR_CPUS];
void smp_send_reschedule(int cpu)
{
os_write_file(cpu_data[cpu].ipi_pipe[1], "R", 1);
num_reschedules_sent++;
}
void smp_send_stop(void)
{
int i;
printk(KERN_INFO "Stopping all CPUs...");
for(i = 0; i < num_online_cpus(); i++){
if(i == current_thread->cpu)
continue;
os_write_file(cpu_data[i].ipi_pipe[1], "S", 1);
}
printk("done\n");
}
static cpumask_t smp_commenced_mask = CPU_MASK_NONE;
static cpumask_t cpu_callin_map = CPU_MASK_NONE;
static int idle_proc(void *cpup)
{
int cpu = (int) cpup, err;
err = os_pipe(cpu_data[cpu].ipi_pipe, 1, 1);
if(err < 0)
panic("CPU#%d failed to create IPI pipe, err = %d", cpu, -err);
os_set_fd_async(cpu_data[cpu].ipi_pipe[0],
current->thread.mode.tt.extern_pid);
wmb();
if (cpu_test_and_set(cpu, cpu_callin_map)) {
printk("huh, CPU#%d already present??\n", cpu);
BUG();
}
while (!cpu_isset(cpu, smp_commenced_mask))
cpu_relax();
cpu_set(cpu, cpu_online_map);
default_idle();
return(0);
}
static struct task_struct *idle_thread(int cpu)
{
struct task_struct *new_task;
unsigned char c;
current->thread.request.u.thread.proc = idle_proc;
current->thread.request.u.thread.arg = (void *) cpu;
new_task = fork_idle(cpu);
if(IS_ERR(new_task))
panic("copy_process failed in idle_thread, error = %ld",
PTR_ERR(new_task));
cpu_tasks[cpu] = ((struct cpu_task)
{ .pid = new_task->thread.mode.tt.extern_pid,
.task = new_task } );
idle_threads[cpu] = new_task;
CHOOSE_MODE(os_write_file(new_task->thread.mode.tt.switch_pipe[1], &c,
sizeof(c)),
({ panic("skas mode doesn't support SMP"); }));
return(new_task);
}
void smp_prepare_cpus(unsigned int maxcpus)
{
struct task_struct *idle;
unsigned long waittime;
int err, cpu, me = smp_processor_id();
int i;
for (i = 0; i < ncpus; ++i)
cpu_set(i, cpu_possible_map);
cpu_clear(me, cpu_online_map);
cpu_set(me, cpu_online_map);
cpu_set(me, cpu_callin_map);
err = os_pipe(cpu_data[me].ipi_pipe, 1, 1);
if(err < 0)
panic("CPU#0 failed to create IPI pipe, errno = %d", -err);
os_set_fd_async(cpu_data[me].ipi_pipe[0],
current->thread.mode.tt.extern_pid);
for(cpu = 1; cpu < ncpus; cpu++){
printk("Booting processor %d...\n", cpu);
idle = idle_thread(cpu);
init_idle(idle, cpu);
waittime = 200000000;
while (waittime-- && !cpu_isset(cpu, cpu_callin_map))
cpu_relax();
if (cpu_isset(cpu, cpu_callin_map))
printk("done\n");
else printk("failed\n");
}
}
void smp_prepare_boot_cpu(void)
{
cpu_set(smp_processor_id(), cpu_online_map);
}
int __cpu_up(unsigned int cpu)
{
cpu_set(cpu, smp_commenced_mask);
while (!cpu_isset(cpu, cpu_online_map))
mb();
return(0);
}
int setup_profiling_timer(unsigned int multiplier)
{
printk(KERN_INFO "setup_profiling_timer\n");
return(0);
}
void smp_call_function_slave(int cpu);
void IPI_handler(int cpu)
{
unsigned char c;
int fd;
fd = cpu_data[cpu].ipi_pipe[0];
while (os_read_file(fd, &c, 1) == 1) {
switch (c) {
case 'C':
smp_call_function_slave(cpu);
break;
case 'R':
set_tsk_need_resched(current);
break;
case 'S':
printk("CPU#%d stopping\n", cpu);
while(1)
pause();
break;
default:
printk("CPU#%d received unknown IPI [%c]!\n", cpu, c);
break;
}
}
}
int hard_smp_processor_id(void)
{
return(pid_to_processor_id(os_getpid()));
}
static DEFINE_SPINLOCK(call_lock);
static atomic_t scf_started;
static atomic_t scf_finished;
static void (*func)(void *info);
static void *info;
void smp_call_function_slave(int cpu)
{
atomic_inc(&scf_started);
(*func)(info);
atomic_inc(&scf_finished);
}
int smp_call_function(void (*_func)(void *info), void *_info, int nonatomic,
int wait)
{
int cpus = num_online_cpus() - 1;
int i;
if (!cpus)
return 0;
/* Can deadlock when called with interrupts disabled */
WARN_ON(irqs_disabled());
spin_lock_bh(&call_lock);
atomic_set(&scf_started, 0);
atomic_set(&scf_finished, 0);
func = _func;
info = _info;
for_each_online_cpu(i)
os_write_file(cpu_data[i].ipi_pipe[1], "C", 1);
while (atomic_read(&scf_started) != cpus)
barrier();
if (wait)
while (atomic_read(&scf_finished) != cpus)
barrier();
spin_unlock_bh(&call_lock);
return 0;
}
#endif
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

163
arch/um/kernel/syscall.c Normal file
View File

@@ -0,0 +1,163 @@
/*
* Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
* Licensed under the GPL
*/
#include "linux/sched.h"
#include "linux/file.h"
#include "linux/smp_lock.h"
#include "linux/mm.h"
#include "linux/utsname.h"
#include "linux/msg.h"
#include "linux/shm.h"
#include "linux/sys.h"
#include "linux/syscalls.h"
#include "linux/unistd.h"
#include "linux/slab.h"
#include "linux/utime.h"
#include "asm/mman.h"
#include "asm/uaccess.h"
#include "kern_util.h"
#include "user_util.h"
#include "sysdep/syscalls.h"
#include "mode_kern.h"
#include "choose-mode.h"
/* Unlocked, I don't care if this is a bit off */
int nsyscalls = 0;
long sys_fork(void)
{
long ret;
current->thread.forking = 1;
ret = do_fork(SIGCHLD, UPT_SP(&current->thread.regs.regs),
&current->thread.regs, 0, NULL, NULL);
current->thread.forking = 0;
return(ret);
}
long sys_vfork(void)
{
long ret;
current->thread.forking = 1;
ret = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD,
UPT_SP(&current->thread.regs.regs),
&current->thread.regs, 0, NULL, NULL);
current->thread.forking = 0;
return(ret);
}
/* common code for old and new mmaps */
long sys_mmap2(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags,
unsigned long fd, unsigned long pgoff)
{
long error = -EBADF;
struct file * file = NULL;
flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
if (!(flags & MAP_ANONYMOUS)) {
file = fget(fd);
if (!file)
goto out;
}
down_write(&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 __user * name)
{
long err;
if (!name)
return -EFAULT;
down_read(&uts_sem);
err = copy_to_user(name, utsname(), sizeof (*name));
up_read(&uts_sem);
return err?-EFAULT:0;
}
long sys_olduname(struct oldold_utsname __user * name)
{
long error;
if (!name)
return -EFAULT;
if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname)))
return -EFAULT;
down_read(&uts_sem);
error = __copy_to_user(&name->sysname, &utsname()->sysname,
__OLD_UTS_LEN);
error |= __put_user(0, name->sysname + __OLD_UTS_LEN);
error |= __copy_to_user(&name->nodename, &utsname()->nodename,
__OLD_UTS_LEN);
error |= __put_user(0, name->nodename + __OLD_UTS_LEN);
error |= __copy_to_user(&name->release, &utsname()->release,
__OLD_UTS_LEN);
error |= __put_user(0, name->release + __OLD_UTS_LEN);
error |= __copy_to_user(&name->version, &utsname()->version,
__OLD_UTS_LEN);
error |= __put_user(0, name->version + __OLD_UTS_LEN);
error |= __copy_to_user(&name->machine, &utsname()->machine,
__OLD_UTS_LEN);
error |= __put_user(0, name->machine + __OLD_UTS_LEN);
up_read(&uts_sem);
error = error ? -EFAULT : 0;
return error;
}
int kernel_execve(const char *filename, char *const argv[], char *const envp[])
{
mm_segment_t fs;
int ret;
fs = get_fs();
set_fs(KERNEL_DS);
ret = um_execve(filename, argv, envp);
set_fs(fs);
return ret;
}

81
arch/um/kernel/sysrq.c Normal file
View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/sched.h"
#include "linux/kernel.h"
#include "linux/module.h"
#include "linux/kallsyms.h"
#include "asm/page.h"
#include "asm/processor.h"
#include "sysrq.h"
#include "user_util.h"
/* Catch non-i386 SUBARCH's. */
#if !defined(CONFIG_UML_X86) || defined(CONFIG_64BIT)
void show_trace(struct task_struct *task, unsigned long * stack)
{
unsigned long addr;
if (!stack) {
stack = (unsigned long*) &stack;
WARN_ON(1);
}
printk("Call Trace: \n");
while (((long) stack & (THREAD_SIZE-1)) != 0) {
addr = *stack;
if (__kernel_text_address(addr)) {
printk("%08lx: [<%08lx>]", (unsigned long) stack, addr);
print_symbol(" %s", addr);
printk("\n");
}
stack++;
}
printk("\n");
}
#endif
/*
* stack dumps generator - this is used by arch-independent code.
* And this is identical to i386 currently.
*/
void dump_stack(void)
{
unsigned long stack;
show_trace(current, &stack);
}
EXPORT_SYMBOL(dump_stack);
/*Stolen from arch/i386/kernel/traps.c */
static const int kstack_depth_to_print = 24;
/* This recently started being used in arch-independent code too, as in
* kernel/sched.c.*/
void show_stack(struct task_struct *task, unsigned long *esp)
{
unsigned long *stack;
int i;
if (esp == NULL) {
if (task != current && task != NULL) {
esp = (unsigned long *) KSTK_ESP(task);
} else {
esp = (unsigned long *) &esp;
}
}
stack = esp;
for(i = 0; i < kstack_depth_to_print; i++) {
if (kstack_end(stack))
break;
if (i && ((i % 8) == 0))
printk("\n ");
printk("%08lx ", *stack++);
}
printk("Call Trace: \n");
show_trace(task, esp);
}

180
arch/um/kernel/time.c Normal file
View File

@@ -0,0 +1,180 @@
/*
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/kernel.h"
#include "linux/module.h"
#include "linux/unistd.h"
#include "linux/stddef.h"
#include "linux/spinlock.h"
#include "linux/time.h"
#include "linux/sched.h"
#include "linux/interrupt.h"
#include "linux/init.h"
#include "linux/delay.h"
#include "linux/hrtimer.h"
#include "asm/irq.h"
#include "asm/param.h"
#include "asm/current.h"
#include "kern_util.h"
#include "user_util.h"
#include "mode.h"
#include "os.h"
int hz(void)
{
return(HZ);
}
/*
* Scheduler clock - returns current time in nanosec units.
*/
unsigned long long sched_clock(void)
{
return (unsigned long long)jiffies_64 * (1000000000 / HZ);
}
static unsigned long long prev_nsecs[NR_CPUS];
#ifdef CONFIG_UML_REAL_TIME_CLOCK
static long long delta[NR_CPUS]; /* Deviation per interval */
#endif
void timer_irq(union uml_pt_regs *regs)
{
unsigned long long ticks = 0;
#ifdef CONFIG_UML_REAL_TIME_CLOCK
int c = cpu();
if(prev_nsecs[c]){
/* We've had 1 tick */
unsigned long long nsecs = os_nsecs();
delta[c] += nsecs - prev_nsecs[c];
prev_nsecs[c] = nsecs;
/* Protect against the host clock being set backwards */
if(delta[c] < 0)
delta[c] = 0;
ticks += (delta[c] * HZ) / BILLION;
delta[c] -= (ticks * BILLION) / HZ;
}
else prev_nsecs[c] = os_nsecs();
#else
ticks = 1;
#endif
while(ticks > 0){
do_IRQ(TIMER_IRQ, regs);
ticks--;
}
}
/* Protects local_offset */
static DEFINE_SPINLOCK(timer_spinlock);
static unsigned long long local_offset = 0;
static inline unsigned long long get_time(void)
{
unsigned long long nsecs;
unsigned long flags;
spin_lock_irqsave(&timer_spinlock, flags);
nsecs = os_nsecs();
nsecs += local_offset;
spin_unlock_irqrestore(&timer_spinlock, flags);
return nsecs;
}
irqreturn_t um_timer(int irq, void *dev)
{
unsigned long long nsecs;
unsigned long flags;
write_seqlock_irqsave(&xtime_lock, flags);
do_timer(1);
nsecs = get_time();
xtime.tv_sec = nsecs / NSEC_PER_SEC;
xtime.tv_nsec = nsecs - xtime.tv_sec * NSEC_PER_SEC;
write_sequnlock_irqrestore(&xtime_lock, flags);
return IRQ_HANDLED;
}
static void register_timer(void)
{
int err;
err = request_irq(TIMER_IRQ, um_timer, IRQF_DISABLED, "timer", NULL);
if(err != 0)
printk(KERN_ERR "register_timer : request_irq failed - "
"errno = %d\n", -err);
err = set_interval(1);
if(err != 0)
printk(KERN_ERR "register_timer : set_interval failed - "
"errno = %d\n", -err);
}
extern void (*late_time_init)(void);
void time_init(void)
{
long long nsecs;
nsecs = os_nsecs();
set_normalized_timespec(&wall_to_monotonic, -nsecs / BILLION,
-nsecs % BILLION);
late_time_init = register_timer;
}
void do_gettimeofday(struct timeval *tv)
{
unsigned long long nsecs = get_time();
tv->tv_sec = nsecs / NSEC_PER_SEC;
/* Careful about calculations here - this was originally done as
* (nsecs - tv->tv_sec * NSEC_PER_SEC) / NSEC_PER_USEC
* which gave bogus (> 1000000) values. Dunno why, suspect gcc
* (4.0.0) miscompiled it, or there's a subtle 64/32-bit conversion
* problem that I missed.
*/
nsecs -= tv->tv_sec * NSEC_PER_SEC;
tv->tv_usec = (unsigned long) nsecs / NSEC_PER_USEC;
}
static inline void set_time(unsigned long long nsecs)
{
unsigned long long now;
unsigned long flags;
spin_lock_irqsave(&timer_spinlock, flags);
now = os_nsecs();
local_offset = nsecs - now;
spin_unlock_irqrestore(&timer_spinlock, flags);
clock_was_set();
}
int do_settimeofday(struct timespec *tv)
{
set_time((unsigned long long) tv->tv_sec * NSEC_PER_SEC + tv->tv_nsec);
return 0;
}
void timer_handler(int sig, union uml_pt_regs *regs)
{
local_irq_disable();
irq_enter();
update_process_times(CHOOSE_MODE(
(UPT_SC(regs) && user_context(UPT_SP(regs))),
(regs)->skas.is_user));
irq_exit();
local_irq_enable();
if(current_thread->cpu == 0)
timer_irq(regs);
}

390
arch/um/kernel/tlb.c Normal file
View File

@@ -0,0 +1,390 @@
/*
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/mm.h"
#include "asm/page.h"
#include "asm/pgalloc.h"
#include "asm/tlbflush.h"
#include "choose-mode.h"
#include "mode_kern.h"
#include "user_util.h"
#include "tlb.h"
#include "mem.h"
#include "mem_user.h"
#include "os.h"
static int add_mmap(unsigned long virt, unsigned long phys, unsigned long len,
int r, int w, int x, struct host_vm_op *ops, int *index,
int last_filled, union mm_context *mmu, void **flush,
int (*do_ops)(union mm_context *, struct host_vm_op *,
int, int, void **))
{
__u64 offset;
struct host_vm_op *last;
int fd, ret = 0;
fd = phys_mapping(phys, &offset);
if(*index != -1){
last = &ops[*index];
if((last->type == MMAP) &&
(last->u.mmap.addr + last->u.mmap.len == virt) &&
(last->u.mmap.r == r) && (last->u.mmap.w == w) &&
(last->u.mmap.x == x) && (last->u.mmap.fd == fd) &&
(last->u.mmap.offset + last->u.mmap.len == offset)){
last->u.mmap.len += len;
return 0;
}
}
if(*index == last_filled){
ret = (*do_ops)(mmu, ops, last_filled, 0, flush);
*index = -1;
}
ops[++*index] = ((struct host_vm_op) { .type = MMAP,
.u = { .mmap = {
.addr = virt,
.len = len,
.r = r,
.w = w,
.x = x,
.fd = fd,
.offset = offset }
} });
return ret;
}
static int add_munmap(unsigned long addr, unsigned long len,
struct host_vm_op *ops, int *index, int last_filled,
union mm_context *mmu, void **flush,
int (*do_ops)(union mm_context *, struct host_vm_op *,
int, int, void **))
{
struct host_vm_op *last;
int ret = 0;
if(*index != -1){
last = &ops[*index];
if((last->type == MUNMAP) &&
(last->u.munmap.addr + last->u.mmap.len == addr)){
last->u.munmap.len += len;
return 0;
}
}
if(*index == last_filled){
ret = (*do_ops)(mmu, ops, last_filled, 0, flush);
*index = -1;
}
ops[++*index] = ((struct host_vm_op) { .type = MUNMAP,
.u = { .munmap = {
.addr = addr,
.len = len } } });
return ret;
}
static int add_mprotect(unsigned long addr, unsigned long len, int r, int w,
int x, struct host_vm_op *ops, int *index,
int last_filled, union mm_context *mmu, void **flush,
int (*do_ops)(union mm_context *, struct host_vm_op *,
int, int, void **))
{
struct host_vm_op *last;
int ret = 0;
if(*index != -1){
last = &ops[*index];
if((last->type == MPROTECT) &&
(last->u.mprotect.addr + last->u.mprotect.len == addr) &&
(last->u.mprotect.r == r) && (last->u.mprotect.w == w) &&
(last->u.mprotect.x == x)){
last->u.mprotect.len += len;
return 0;
}
}
if(*index == last_filled){
ret = (*do_ops)(mmu, ops, last_filled, 0, flush);
*index = -1;
}
ops[++*index] = ((struct host_vm_op) { .type = MPROTECT,
.u = { .mprotect = {
.addr = addr,
.len = len,
.r = r,
.w = w,
.x = x } } });
return ret;
}
#define ADD_ROUND(n, inc) (((n) + (inc)) & ~((inc) - 1))
void fix_range_common(struct mm_struct *mm, unsigned long start_addr,
unsigned long end_addr, int force,
int (*do_ops)(union mm_context *, struct host_vm_op *,
int, int, void **))
{
pgd_t *npgd;
pud_t *npud;
pmd_t *npmd;
pte_t *npte;
union mm_context *mmu = &mm->context;
unsigned long addr, end;
int r, w, x;
struct host_vm_op ops[1];
void *flush = NULL;
int op_index = -1, last_op = ARRAY_SIZE(ops) - 1;
int ret = 0;
if(mm == NULL)
return;
ops[0].type = NONE;
for(addr = start_addr; addr < end_addr && !ret;){
npgd = pgd_offset(mm, addr);
if(!pgd_present(*npgd)){
end = ADD_ROUND(addr, PGDIR_SIZE);
if(end > end_addr)
end = end_addr;
if(force || pgd_newpage(*npgd)){
ret = add_munmap(addr, end - addr, ops,
&op_index, last_op, mmu,
&flush, do_ops);
pgd_mkuptodate(*npgd);
}
addr = end;
continue;
}
npud = pud_offset(npgd, addr);
if(!pud_present(*npud)){
end = ADD_ROUND(addr, PUD_SIZE);
if(end > end_addr)
end = end_addr;
if(force || pud_newpage(*npud)){
ret = add_munmap(addr, end - addr, ops,
&op_index, last_op, mmu,
&flush, do_ops);
pud_mkuptodate(*npud);
}
addr = end;
continue;
}
npmd = pmd_offset(npud, addr);
if(!pmd_present(*npmd)){
end = ADD_ROUND(addr, PMD_SIZE);
if(end > end_addr)
end = end_addr;
if(force || pmd_newpage(*npmd)){
ret = add_munmap(addr, end - addr, ops,
&op_index, last_op, mmu,
&flush, do_ops);
pmd_mkuptodate(*npmd);
}
addr = end;
continue;
}
npte = pte_offset_kernel(npmd, addr);
r = pte_read(*npte);
w = pte_write(*npte);
x = pte_exec(*npte);
if (!pte_young(*npte)) {
r = 0;
w = 0;
} else if (!pte_dirty(*npte)) {
w = 0;
}
if(force || pte_newpage(*npte)){
if(pte_present(*npte))
ret = add_mmap(addr,
pte_val(*npte) & PAGE_MASK,
PAGE_SIZE, r, w, x, ops,
&op_index, last_op, mmu,
&flush, do_ops);
else ret = add_munmap(addr, PAGE_SIZE, ops,
&op_index, last_op, mmu,
&flush, do_ops);
}
else if(pte_newprot(*npte))
ret = add_mprotect(addr, PAGE_SIZE, r, w, x, ops,
&op_index, last_op, mmu,
&flush, do_ops);
*npte = pte_mkuptodate(*npte);
addr += PAGE_SIZE;
}
if(!ret)
ret = (*do_ops)(mmu, ops, op_index, 1, &flush);
/* This is not an else because ret is modified above */
if(ret) {
printk("fix_range_common: failed, killing current process\n");
force_sig(SIGKILL, current);
}
}
int flush_tlb_kernel_range_common(unsigned long start, unsigned long end)
{
struct mm_struct *mm;
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
unsigned long addr, last;
int updated = 0, err;
mm = &init_mm;
for(addr = start; addr < end;){
pgd = pgd_offset(mm, addr);
if(!pgd_present(*pgd)){
last = ADD_ROUND(addr, PGDIR_SIZE);
if(last > end)
last = end;
if(pgd_newpage(*pgd)){
updated = 1;
err = os_unmap_memory((void *) addr,
last - addr);
if(err < 0)
panic("munmap failed, errno = %d\n",
-err);
}
addr = last;
continue;
}
pud = pud_offset(pgd, addr);
if(!pud_present(*pud)){
last = ADD_ROUND(addr, PUD_SIZE);
if(last > end)
last = end;
if(pud_newpage(*pud)){
updated = 1;
err = os_unmap_memory((void *) addr,
last - addr);
if(err < 0)
panic("munmap failed, errno = %d\n",
-err);
}
addr = last;
continue;
}
pmd = pmd_offset(pud, addr);
if(!pmd_present(*pmd)){
last = ADD_ROUND(addr, PMD_SIZE);
if(last > end)
last = end;
if(pmd_newpage(*pmd)){
updated = 1;
err = os_unmap_memory((void *) addr,
last - addr);
if(err < 0)
panic("munmap failed, errno = %d\n",
-err);
}
addr = last;
continue;
}
pte = pte_offset_kernel(pmd, addr);
if(!pte_present(*pte) || pte_newpage(*pte)){
updated = 1;
err = os_unmap_memory((void *) addr,
PAGE_SIZE);
if(err < 0)
panic("munmap failed, errno = %d\n",
-err);
if(pte_present(*pte))
map_memory(addr,
pte_val(*pte) & PAGE_MASK,
PAGE_SIZE, 1, 1, 1);
}
else if(pte_newprot(*pte)){
updated = 1;
os_protect_memory((void *) addr, PAGE_SIZE, 1, 1, 1);
}
addr += PAGE_SIZE;
}
return(updated);
}
pgd_t *pgd_offset_proc(struct mm_struct *mm, unsigned long address)
{
return(pgd_offset(mm, address));
}
pud_t *pud_offset_proc(pgd_t *pgd, unsigned long address)
{
return(pud_offset(pgd, address));
}
pmd_t *pmd_offset_proc(pud_t *pud, unsigned long address)
{
return(pmd_offset(pud, address));
}
pte_t *pte_offset_proc(pmd_t *pmd, unsigned long address)
{
return(pte_offset_kernel(pmd, address));
}
pte_t *addr_pte(struct task_struct *task, unsigned long addr)
{
pgd_t *pgd = pgd_offset(task->mm, addr);
pud_t *pud = pud_offset(pgd, addr);
pmd_t *pmd = pmd_offset(pud, addr);
return(pte_offset_map(pmd, addr));
}
void flush_tlb_page(struct vm_area_struct *vma, unsigned long address)
{
address &= PAGE_MASK;
flush_tlb_range(vma, address, address + PAGE_SIZE);
}
void flush_tlb_all(void)
{
flush_tlb_mm(current->mm);
}
void flush_tlb_kernel_range(unsigned long start, unsigned long end)
{
CHOOSE_MODE_PROC(flush_tlb_kernel_range_tt,
flush_tlb_kernel_range_common, start, end);
}
void flush_tlb_kernel_vm(void)
{
CHOOSE_MODE(flush_tlb_kernel_vm_tt(),
flush_tlb_kernel_range_common(start_vm, end_vm));
}
void __flush_tlb_one(unsigned long addr)
{
CHOOSE_MODE_PROC(__flush_tlb_one_tt, __flush_tlb_one_skas, addr);
}
void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
unsigned long end)
{
CHOOSE_MODE_PROC(flush_tlb_range_tt, flush_tlb_range_skas, vma, start,
end);
}
void flush_tlb_mm(struct mm_struct *mm)
{
CHOOSE_MODE_PROC(flush_tlb_mm_tt, flush_tlb_mm_skas, mm);
}
void force_flush_all(void)
{
CHOOSE_MODE(force_flush_all_tt(), force_flush_all_skas());
}

258
arch/um/kernel/trap.c Normal file
View File

@@ -0,0 +1,258 @@
/*
* Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/kernel.h"
#include "asm/errno.h"
#include "linux/sched.h"
#include "linux/mm.h"
#include "linux/spinlock.h"
#include "linux/init.h"
#include "linux/ptrace.h"
#include "asm/semaphore.h"
#include "asm/pgtable.h"
#include "asm/pgalloc.h"
#include "asm/tlbflush.h"
#include "asm/a.out.h"
#include "asm/current.h"
#include "asm/irq.h"
#include "sysdep/sigcontext.h"
#include "user_util.h"
#include "kern_util.h"
#include "kern.h"
#include "chan_kern.h"
#include "mconsole_kern.h"
#include "mem.h"
#include "mem_kern.h"
#include "sysdep/sigcontext.h"
#include "sysdep/ptrace.h"
#include "os.h"
#ifdef CONFIG_MODE_SKAS
#include "skas.h"
#endif
#include "os.h"
/* Note this is constrained to return 0, -EFAULT, -EACCESS, -ENOMEM by segv(). */
int handle_page_fault(unsigned long address, unsigned long ip,
int is_write, int is_user, int *code_out)
{
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
int err = -EFAULT;
*code_out = SEGV_MAPERR;
/* If the fault was during atomic operation, don't take the fault, just
* fail. */
if (in_atomic())
goto out_nosemaphore;
down_read(&mm->mmap_sem);
vma = find_vma(mm, address);
if(!vma)
goto out;
else if(vma->vm_start <= address)
goto good_area;
else if(!(vma->vm_flags & VM_GROWSDOWN))
goto out;
else if(is_user && !ARCH_IS_STACKGROW(address))
goto out;
else if(expand_stack(vma, address))
goto out;
good_area:
*code_out = SEGV_ACCERR;
if(is_write && !(vma->vm_flags & VM_WRITE))
goto out;
/* Don't require VM_READ|VM_EXEC for write faults! */
if(!is_write && !(vma->vm_flags & (VM_READ | VM_EXEC)))
goto out;
do {
survive:
switch (handle_mm_fault(mm, vma, address, is_write)){
case VM_FAULT_MINOR:
current->min_flt++;
break;
case VM_FAULT_MAJOR:
current->maj_flt++;
break;
case VM_FAULT_SIGBUS:
err = -EACCES;
goto out;
case VM_FAULT_OOM:
err = -ENOMEM;
goto out_of_memory;
default:
BUG();
}
pgd = pgd_offset(mm, address);
pud = pud_offset(pgd, address);
pmd = pmd_offset(pud, address);
pte = pte_offset_kernel(pmd, address);
} while(!pte_present(*pte));
err = 0;
/* The below warning was added in place of
* pte_mkyoung(); if (is_write) pte_mkdirty();
* If it's triggered, we'd see normally a hang here (a clean pte is
* marked read-only to emulate the dirty bit).
* However, the generic code can mark a PTE writable but clean on a
* concurrent read fault, triggering this harmlessly. So comment it out.
*/
#if 0
WARN_ON(!pte_young(*pte) || (is_write && !pte_dirty(*pte)));
#endif
flush_tlb_page(vma, address);
out:
up_read(&mm->mmap_sem);
out_nosemaphore:
return(err);
/*
* We ran out of memory, or some other thing happened to us that made
* us unable to handle the page fault gracefully.
*/
out_of_memory:
if (is_init(current)) {
up_read(&mm->mmap_sem);
yield();
down_read(&mm->mmap_sem);
goto survive;
}
goto out;
}
static void bad_segv(struct faultinfo fi, unsigned long ip)
{
struct siginfo si;
si.si_signo = SIGSEGV;
si.si_code = SEGV_ACCERR;
si.si_addr = (void __user *) FAULT_ADDRESS(fi);
current->thread.arch.faultinfo = fi;
force_sig_info(SIGSEGV, &si, current);
}
static void segv_handler(int sig, union uml_pt_regs *regs)
{
struct faultinfo * fi = UPT_FAULTINFO(regs);
if(UPT_IS_USER(regs) && !SEGV_IS_FIXABLE(fi)){
bad_segv(*fi, UPT_IP(regs));
return;
}
segv(*fi, UPT_IP(regs), UPT_IS_USER(regs), regs);
}
/*
* We give a *copy* of the faultinfo in the regs to segv.
* This must be done, since nesting SEGVs could overwrite
* the info in the regs. A pointer to the info then would
* give us bad data!
*/
unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, void *sc)
{
struct siginfo si;
void *catcher;
int err;
int is_write = FAULT_WRITE(fi);
unsigned long address = FAULT_ADDRESS(fi);
if(!is_user && (address >= start_vm) && (address < end_vm)){
flush_tlb_kernel_vm();
return(0);
}
else if(current->mm == NULL)
panic("Segfault with no mm");
if (SEGV_IS_FIXABLE(&fi) || SEGV_MAYBE_FIXABLE(&fi))
err = handle_page_fault(address, ip, is_write, is_user, &si.si_code);
else {
err = -EFAULT;
/* A thread accessed NULL, we get a fault, but CR2 is invalid.
* This code is used in __do_copy_from_user() of TT mode. */
address = 0;
}
catcher = current->thread.fault_catcher;
if(!err)
return(0);
else if(catcher != NULL){
current->thread.fault_addr = (void *) address;
do_longjmp(catcher, 1);
}
else if(current->thread.fault_addr != NULL)
panic("fault_addr set but no fault catcher");
else if(!is_user && arch_fixup(ip, sc))
return(0);
if(!is_user)
panic("Kernel mode fault at addr 0x%lx, ip 0x%lx",
address, ip);
if (err == -EACCES) {
si.si_signo = SIGBUS;
si.si_errno = 0;
si.si_code = BUS_ADRERR;
si.si_addr = (void __user *)address;
current->thread.arch.faultinfo = fi;
force_sig_info(SIGBUS, &si, current);
} else if (err == -ENOMEM) {
printk("VM: killing process %s\n", current->comm);
do_exit(SIGKILL);
} else {
BUG_ON(err != -EFAULT);
si.si_signo = SIGSEGV;
si.si_addr = (void __user *) address;
current->thread.arch.faultinfo = fi;
force_sig_info(SIGSEGV, &si, current);
}
return(0);
}
void relay_signal(int sig, union uml_pt_regs *regs)
{
if(arch_handle_signal(sig, regs))
return;
if(!UPT_IS_USER(regs)){
if(sig == SIGBUS)
printk("Bus error - the /dev/shm or /tmp mount likely "
"just ran out of space\n");
panic("Kernel mode signal %d", sig);
}
current->thread.arch.faultinfo = *UPT_FAULTINFO(regs);
force_sig(sig, current);
}
static void bus_handler(int sig, union uml_pt_regs *regs)
{
if(current->thread.fault_catcher != NULL)
do_longjmp(current->thread.fault_catcher, 1);
else relay_signal(sig, regs);
}
static void winch(int sig, union uml_pt_regs *regs)
{
do_IRQ(WINCH_IRQ, regs);
}
const struct kern_handlers handlinfo_kern = {
.relay_signal = relay_signal,
.winch = winch,
.bus_handler = bus_handler,
.page_fault = segv_handler,
.sigio_handler = sigio_handler,
.timer_handler = timer_handler
};
void trap_init(void)
{
}

View File

@@ -0,0 +1,14 @@
#
# Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com)
# Licensed under the GPL
#
obj-y = exec_kern.o exec_user.o gdb.o ksyms.o mem.o mem_user.o process_kern.o \
syscall_kern.o syscall_user.o tlb.o tracer.o trap_user.o \
uaccess.o uaccess_user.o
obj-$(CONFIG_PT_PROXY) += gdb_kern.o ptproxy/
USER_OBJS := gdb.o tracer.o
include arch/um/scripts/Makefile.rules

View File

@@ -0,0 +1,85 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/kernel.h"
#include "linux/mm.h"
#include "asm/signal.h"
#include "asm/ptrace.h"
#include "asm/uaccess.h"
#include "asm/pgalloc.h"
#include "asm/tlbflush.h"
#include "user_util.h"
#include "kern_util.h"
#include "irq_user.h"
#include "mem_user.h"
#include "os.h"
#include "tlb.h"
#include "mode.h"
static int exec_tramp(void *sig_stack)
{
init_new_thread_stack(sig_stack, NULL);
init_new_thread_signals();
os_stop_process(os_getpid());
return(0);
}
void flush_thread_tt(void)
{
unsigned long stack;
int new_pid;
stack = alloc_stack(0, 0);
if(stack == 0){
printk(KERN_ERR
"flush_thread : failed to allocate temporary stack\n");
do_exit(SIGKILL);
}
new_pid = start_fork_tramp(task_stack_page(current), stack, 0, exec_tramp);
if(new_pid < 0){
printk(KERN_ERR
"flush_thread : new thread failed, errno = %d\n",
-new_pid);
do_exit(SIGKILL);
}
if(current_thread->cpu == 0)
forward_interrupts(new_pid);
current->thread.request.op = OP_EXEC;
current->thread.request.u.exec.pid = new_pid;
unprotect_stack((unsigned long) current_thread);
os_usr1_process(os_getpid());
change_sig(SIGUSR1, 1);
change_sig(SIGUSR1, 0);
enable_timer();
free_page(stack);
protect_memory(uml_reserved, high_physmem - uml_reserved, 1, 1, 0, 1);
task_protections((unsigned long) current_thread);
force_flush_all();
unblock_signals();
}
void start_thread_tt(struct pt_regs *regs, unsigned long eip,
unsigned long esp)
{
set_fs(USER_DS);
flush_tlb_mm(current->mm);
PT_REGS_IP(regs) = eip;
PT_REGS_SP(regs) = esp;
PT_FIX_EXEC_STACK(esp);
}
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sched.h>
#include <errno.h>
#include <sys/wait.h>
#include <signal.h>
#include "user_util.h"
#include "kern_util.h"
#include "user.h"
#include "ptrace_user.h"
#include "os.h"
void do_exec(int old_pid, int new_pid)
{
unsigned long regs[FRAME_SIZE];
int err;
if((ptrace(PTRACE_ATTACH, new_pid, 0, 0) < 0) ||
(ptrace(PTRACE_CONT, new_pid, 0, 0) < 0))
tracer_panic("do_exec failed to attach proc - errno = %d",
errno);
CATCH_EINTR(err = waitpid(new_pid, 0, WUNTRACED));
if (err < 0)
tracer_panic("do_exec failed to attach proc in waitpid - errno = %d",
errno);
if(ptrace_getregs(old_pid, regs) < 0)
tracer_panic("do_exec failed to get registers - errno = %d",
errno);
os_kill_ptraced_process(old_pid, 0);
if (ptrace(PTRACE_OLDSETOPTIONS, new_pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0)
tracer_panic("do_exec: PTRACE_SETOPTIONS failed, errno = %d", errno);
if(ptrace_setregs(new_pid, regs) < 0)
tracer_panic("do_exec failed to start new proc - errno = %d",
errno);
}
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

279
arch/um/kernel/tt/gdb.c Normal file
View File

@@ -0,0 +1,279 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include "ptrace_user.h"
#include "uml-config.h"
#include "kern_constants.h"
#include "chan_user.h"
#include "init.h"
#include "user.h"
#include "debug.h"
#include "kern_util.h"
#include "user_util.h"
#include "tt.h"
#include "sysdep/thread.h"
#include "os.h"
extern int debugger_pid;
extern int debugger_fd;
extern int debugger_parent;
int detach(int pid, int sig)
{
return(ptrace(PTRACE_DETACH, pid, 0, sig));
}
int attach(int pid)
{
int err;
err = ptrace(PTRACE_ATTACH, pid, 0, 0);
if(err < 0) return(-errno);
else return(err);
}
int cont(int pid)
{
return(ptrace(PTRACE_CONT, pid, 0, 0));
}
#ifdef UML_CONFIG_PT_PROXY
int debugger_signal(int status, pid_t pid)
{
return(debugger_proxy(status, pid));
}
void child_signal(pid_t pid, int status)
{
child_proxy(pid, status);
}
static void gdb_announce(char *dev_name, int dev)
{
printf("gdb assigned device '%s'\n", dev_name);
}
static struct chan_opts opts = {
.announce = gdb_announce,
.xterm_title = "UML kernel debugger",
.raw = 0,
.tramp_stack = 0,
.in_kernel = 0,
};
/* Accessed by the tracing thread, which automatically serializes access */
static void *xterm_data;
static int xterm_fd;
extern void *xterm_init(char *, int, struct chan_opts *);
extern int xterm_open(int, int, int, void *, char **);
extern void xterm_close(int, void *);
int open_gdb_chan(void)
{
char stack[UM_KERN_PAGE_SIZE], *dummy;
opts.tramp_stack = (unsigned long) stack;
xterm_data = xterm_init("", 0, &opts);
xterm_fd = xterm_open(1, 1, 1, xterm_data, &dummy);
return(xterm_fd);
}
static void exit_debugger_cb(void *unused)
{
if(debugger_pid != -1){
if(gdb_pid != -1){
fake_child_exit();
gdb_pid = -1;
}
else kill_child_dead(debugger_pid);
debugger_pid = -1;
if(debugger_parent != -1)
detach(debugger_parent, SIGINT);
}
if(xterm_data != NULL) xterm_close(xterm_fd, xterm_data);
}
static void exit_debugger(void)
{
initial_thread_cb(exit_debugger_cb, NULL);
}
__uml_exitcall(exit_debugger);
struct gdb_data {
char *str;
int err;
};
static void config_gdb_cb(void *arg)
{
struct gdb_data *data = arg;
void *task;
int pid;
data->err = -1;
if(debugger_pid != -1) exit_debugger_cb(NULL);
if(!strncmp(data->str, "pid,", strlen("pid,"))){
data->str += strlen("pid,");
pid = strtoul(data->str, NULL, 0);
task = cpu_tasks[0].task;
debugger_pid = attach_debugger(TASK_EXTERN_PID(task), pid, 0);
if(debugger_pid != -1){
data->err = 0;
gdb_pid = pid;
}
return;
}
data->err = 0;
debugger_pid = start_debugger(linux_prog, 0, 0, &debugger_fd);
init_proxy(debugger_pid, 0, 0);
}
int gdb_config(char *str, char **error_out)
{
struct gdb_data data;
if(*str++ != '=') return(-1);
data.str = str;
initial_thread_cb(config_gdb_cb, &data);
return(data.err);
}
void remove_gdb_cb(void *unused)
{
exit_debugger_cb(NULL);
}
int gdb_remove(int unused, char **error_out)
{
initial_thread_cb(remove_gdb_cb, NULL);
return 0;
}
void signal_usr1(int sig)
{
if(debugger_pid != -1){
printf("The debugger is already running\n");
return;
}
debugger_pid = start_debugger(linux_prog, 0, 0, &debugger_fd);
init_proxy(debugger_pid, 0, 0);
}
int init_ptrace_proxy(int idle_pid, int startup, int stop)
{
int pid, status;
pid = start_debugger(linux_prog, startup, stop, &debugger_fd);
status = wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT, NULL);
if(pid < 0){
cont(idle_pid);
return(-1);
}
init_proxy(pid, 1, status);
return(pid);
}
int attach_debugger(int idle_pid, int pid, int stop)
{
int status = 0, err;
err = attach(pid);
if(err < 0){
printf("Failed to attach pid %d, errno = %d\n", pid, -err);
return(-1);
}
if(stop) status = wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT, NULL);
init_proxy(pid, 1, status);
return(pid);
}
#ifdef notdef /* Put this back in when it does something useful */
static int __init uml_gdb_init_setup(char *line, int *add)
{
gdb_init = uml_strdup(line);
return 0;
}
__uml_setup("gdb=", uml_gdb_init_setup,
"gdb=<channel description>\n\n"
);
#endif
static int __init uml_gdb_pid_setup(char *line, int *add)
{
gdb_pid = strtoul(line, NULL, 0);
*add = 0;
return 0;
}
__uml_setup("gdb-pid=", uml_gdb_pid_setup,
"gdb-pid=<pid>\n"
" gdb-pid is used to attach an external debugger to UML. This may be\n"
" an already-running gdb or a debugger-like process like strace.\n\n"
);
#else
int debugger_signal(int status, pid_t pid){ return(0); }
void child_signal(pid_t pid, int status){ }
int init_ptrace_proxy(int idle_pid, int startup, int stop)
{
printf("debug requested when CONFIG_PT_PROXY is off\n");
kill_child_dead(idle_pid);
exit(1);
}
void signal_usr1(int sig)
{
printf("debug requested when CONFIG_PT_PROXY is off\n");
}
int attach_debugger(int idle_pid, int pid, int stop)
{
printf("attach_debugger called when CONFIG_PT_PROXY "
"is off\n");
return(-1);
}
int config_gdb(char *str)
{
return(-1);
}
int remove_gdb(void)
{
return(-1);
}
int init_parent_proxy(int pid)
{
return(-1);
}
void debugger_parent_signal(int status, int pid)
{
}
#endif
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/init.h"
#include "mconsole_kern.h"
#ifdef CONFIG_MCONSOLE
extern int gdb_config(char *str, char **error_out);
extern int gdb_remove(int n, char **error_out);
static struct mc_device gdb_mc = {
.list = INIT_LIST_HEAD(gdb_mc.list),
.name = "gdb",
.config = gdb_config,
.remove = gdb_remove,
};
int gdb_mc_init(void)
{
mconsole_register_dev(&gdb_mc);
return(0);
}
__initcall(gdb_mc_init);
#endif
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#ifndef __MODE_TT_H__
#define __MODE_TT_H__
#include "sysdep/ptrace.h"
enum { OP_NONE, OP_EXEC, OP_FORK, OP_TRACE_ON, OP_REBOOT, OP_HALT, OP_CB };
extern int tracing_pid;
extern int tracer(int (*init_proc)(void *), void *sp);
extern void sig_handler_common_tt(int sig, void *sc);
extern void syscall_handler_tt(int sig, union uml_pt_regs *regs);
extern void reboot_tt(void);
extern void halt_tt(void);
extern int is_tracer_winch(int pid, int fd, void *data);
extern void kill_off_processes_tt(void);
#endif
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#ifndef __TT_MODE_KERN_H__
#define __TT_MODE_KERN_H__
#include "linux/sched.h"
#include "asm/page.h"
#include "asm/ptrace.h"
#include "asm/uaccess.h"
extern void switch_to_tt(void *prev, void *next);
extern void flush_thread_tt(void);
extern void start_thread_tt(struct pt_regs *regs, unsigned long eip,
unsigned long esp);
extern int copy_thread_tt(int nr, unsigned long clone_flags, unsigned long sp,
unsigned long stack_top, struct task_struct *p,
struct pt_regs *regs);
extern void release_thread_tt(struct task_struct *task);
extern void initial_thread_cb_tt(void (*proc)(void *), void *arg);
extern void init_idle_tt(void);
extern void flush_tlb_kernel_range_tt(unsigned long start, unsigned long end);
extern void flush_tlb_kernel_vm_tt(void);
extern void __flush_tlb_one_tt(unsigned long addr);
extern void flush_tlb_range_tt(struct vm_area_struct *vma,
unsigned long start, unsigned long end);
extern void flush_tlb_mm_tt(struct mm_struct *mm);
extern void force_flush_all_tt(void);
extern long execute_syscall_tt(void *r);
extern void before_mem_tt(unsigned long brk_start);
extern unsigned long set_task_sizes_tt(int arg, unsigned long *host_size_out,
unsigned long *task_size_out);
extern int start_uml_tt(void);
extern int external_pid_tt(struct task_struct *task);
extern int thread_pid_tt(struct task_struct *task);
#define kmem_end_tt (host_task_size - ABOVE_KMEM)
#endif
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

29
arch/um/kernel/tt/ksyms.c Normal file
View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/module.h"
#include "asm/uaccess.h"
#include "mode.h"
EXPORT_SYMBOL(__do_copy_from_user);
EXPORT_SYMBOL(__do_copy_to_user);
EXPORT_SYMBOL(__do_strncpy_from_user);
EXPORT_SYMBOL(__do_strnlen_user);
EXPORT_SYMBOL(__do_clear_user);
EXPORT_SYMBOL(clear_user_tt);
EXPORT_SYMBOL(tracing_pid);
EXPORT_SYMBOL(honeypot);
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

35
arch/um/kernel/tt/mem.c Normal file
View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/stddef.h"
#include "linux/mm.h"
#include "asm/uaccess.h"
#include "mem_user.h"
#include "kern_util.h"
#include "user_util.h"
#include "kern.h"
#include "tt.h"
void before_mem_tt(unsigned long brk_start)
{
if(debug)
remap_data(UML_ROUND_DOWN(&_stext), UML_ROUND_UP(&_etext), 1);
remap_data(UML_ROUND_DOWN(&_sdata), UML_ROUND_UP(&_edata), 1);
remap_data(UML_ROUND_DOWN(&__bss_start), UML_ROUND_UP(&_end), 1);
}
#define SIZE ((CONFIG_NEST_LEVEL + CONFIG_KERNEL_HALF_GIGS) * 0x20000000)
#define START (CONFIG_TOP_ADDR - SIZE)
unsigned long set_task_sizes_tt(unsigned long *task_size_out)
{
unsigned long host_task_size;
/* Round up to the nearest 4M */
host_task_size = ROUND_4M((unsigned long) &host_task_size);
*task_size_out = START;
return host_task_size;
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#include "tt.h"
#include "mem_user.h"
#include "user_util.h"
#include "os.h"
void remap_data(void *segment_start, void *segment_end, int w)
{
void *addr;
unsigned long size;
int data, prot;
if(w) prot = PROT_WRITE;
else prot = 0;
prot |= PROT_READ | PROT_EXEC;
size = (unsigned long) segment_end -
(unsigned long) segment_start;
data = create_mem_file(size);
addr = mmap(NULL, size, PROT_WRITE | PROT_READ, MAP_SHARED, data, 0);
if(addr == MAP_FAILED){
perror("mapping new data segment");
exit(1);
}
memcpy(addr, segment_start, size);
if(switcheroo(data, prot, addr, segment_start, size) < 0){
printf("switcheroo failed\n");
exit(1);
}
}
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

View File

@@ -0,0 +1,461 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/sched.h"
#include "linux/signal.h"
#include "linux/kernel.h"
#include "linux/interrupt.h"
#include "linux/ptrace.h"
#include "asm/system.h"
#include "asm/pgalloc.h"
#include "asm/ptrace.h"
#include "asm/tlbflush.h"
#include "irq_user.h"
#include "kern_util.h"
#include "user_util.h"
#include "os.h"
#include "kern.h"
#include "sigcontext.h"
#include "mem_user.h"
#include "tlb.h"
#include "mode.h"
#include "mode_kern.h"
#include "init.h"
#include "tt.h"
void switch_to_tt(void *prev, void *next)
{
struct task_struct *from, *to, *prev_sched;
unsigned long flags;
int err, vtalrm, alrm, prof, cpu;
char c;
from = prev;
to = next;
cpu = task_thread_info(from)->cpu;
if(cpu == 0)
forward_interrupts(to->thread.mode.tt.extern_pid);
#ifdef CONFIG_SMP
forward_ipi(cpu_data[cpu].ipi_pipe[0], to->thread.mode.tt.extern_pid);
#endif
local_irq_save(flags);
vtalrm = change_sig(SIGVTALRM, 0);
alrm = change_sig(SIGALRM, 0);
prof = change_sig(SIGPROF, 0);
forward_pending_sigio(to->thread.mode.tt.extern_pid);
c = 0;
/* Notice that here we "up" the semaphore on which "to" is waiting, and
* below (the read) we wait on this semaphore (which is implemented by
* switch_pipe) and go sleeping. Thus, after that, we have resumed in
* "to", and can't use any more the value of "from" (which is outdated),
* nor the value in "to" (since it was the task which stole us the CPU,
* which we don't care about). */
err = os_write_file(to->thread.mode.tt.switch_pipe[1], &c, sizeof(c));
if(err != sizeof(c))
panic("write of switch_pipe failed, err = %d", -err);
if(from->thread.mode.tt.switch_pipe[0] == -1)
os_kill_process(os_getpid(), 0);
err = os_read_file(from->thread.mode.tt.switch_pipe[0], &c, sizeof(c));
if(err != sizeof(c))
panic("read of switch_pipe failed, errno = %d", -err);
/* If the process that we have just scheduled away from has exited,
* then it needs to be killed here. The reason is that, even though
* it will kill itself when it next runs, that may be too late. Its
* stack will be freed, possibly before then, and if that happens,
* we have a use-after-free situation. So, it gets killed here
* in case it has not already killed itself.
*/
prev_sched = current->thread.prev_sched;
if(prev_sched->thread.mode.tt.switch_pipe[0] == -1)
os_kill_process(prev_sched->thread.mode.tt.extern_pid, 1);
change_sig(SIGVTALRM, vtalrm);
change_sig(SIGALRM, alrm);
change_sig(SIGPROF, prof);
arch_switch_to_tt(prev_sched, current);
flush_tlb_all();
local_irq_restore(flags);
}
void release_thread_tt(struct task_struct *task)
{
int pid = task->thread.mode.tt.extern_pid;
/*
* We first have to kill the other process, before
* closing its switch_pipe. Else it might wake up
* and receive "EOF" before we could kill it.
*/
if(os_getpid() != pid)
os_kill_process(pid, 0);
os_close_file(task->thread.mode.tt.switch_pipe[0]);
os_close_file(task->thread.mode.tt.switch_pipe[1]);
/* use switch_pipe as flag: thread is released */
task->thread.mode.tt.switch_pipe[0] = -1;
}
void suspend_new_thread(int fd)
{
int err;
char c;
os_stop_process(os_getpid());
err = os_read_file(fd, &c, sizeof(c));
if(err != sizeof(c))
panic("read failed in suspend_new_thread, err = %d", -err);
}
void schedule_tail(struct task_struct *prev);
static void new_thread_handler(int sig)
{
unsigned long disable;
int (*fn)(void *);
void *arg;
fn = current->thread.request.u.thread.proc;
arg = current->thread.request.u.thread.arg;
UPT_SC(&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();
enable_timer();
free_page(current->thread.temp_stack);
set_cmdline("(kernel thread)");
change_sig(SIGUSR1, 1);
change_sig(SIGPROF, 1);
local_irq_enable();
if(!run_kernel_thread(fn, arg, &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(task_stack_page(p), stack, clone_flags, tramp);
if(new_pid < 0){
printk(KERN_ERR "copy_thread : clone failed - errno = %d\n",
-new_pid);
return(new_pid);
}
if(current->thread.forking){
sc_to_sc(UPT_SC(&p->thread.regs.regs), UPT_SC(&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(task)->cpu].pid = pid;
break;
case OP_FORK:
attach_process(thread->request.u.fork.pid);
break;
case OP_CB:
(*thread->request.u.cb.proc)(thread->request.u.cb.arg);
break;
case OP_REBOOT:
case OP_HALT:
break;
default:
tracer_panic("Bad op in do_proc_op");
break;
}
thread->request.op = OP_NONE;
return(op);
}
void init_idle_tt(void)
{
default_idle();
}
extern void start_kernel(void);
static int start_kernel_proc(void *unused)
{
int pid;
block_signals();
pid = os_getpid();
cpu_tasks[0].pid = pid;
cpu_tasks[0].task = current;
#ifdef CONFIG_SMP
cpu_online_map = cpumask_of_cpu(0);
#endif
if(debug) os_stop_process(pid);
start_kernel();
return(0);
}
void set_tracing(void *task, int tracing)
{
((struct task_struct *) task)->thread.mode.tt.tracing = tracing;
}
int is_tracing(void *t)
{
return (((struct task_struct *) t)->thread.mode.tt.tracing);
}
int set_user_mode(void *t)
{
struct task_struct *task;
task = t ? t : current;
if(task->thread.mode.tt.tracing)
return(1);
task->thread.request.op = OP_TRACE_ON;
os_usr1_process(os_getpid());
return(0);
}
void set_init_pid(int pid)
{
int err;
init_task.thread.mode.tt.extern_pid = pid;
err = os_pipe(init_task.thread.mode.tt.switch_pipe, 1, 1);
if(err)
panic("Can't create switch pipe for init_task, errno = %d",
-err);
}
int start_uml_tt(void)
{
void *sp;
int pages;
pages = (1 << CONFIG_KERNEL_STACK_ORDER);
sp = task_stack_page(&init_task) +
pages * PAGE_SIZE - sizeof(unsigned long);
return(tracer(start_kernel_proc, sp));
}
int external_pid_tt(struct task_struct *task)
{
return(task->thread.mode.tt.extern_pid);
}
int thread_pid_tt(struct task_struct *task)
{
return(task->thread.mode.tt.extern_pid);
}
int is_valid_pid(int pid)
{
struct task_struct *task;
read_lock(&tasklist_lock);
for_each_process(task){
if(task->thread.mode.tt.extern_pid == pid){
read_unlock(&tasklist_lock);
return(1);
}
}
read_unlock(&tasklist_lock);
return(0);
}

View File

@@ -0,0 +1,10 @@
#
# Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
# Licensed under the GPL
#
obj-y = proxy.o ptrace.o sysdep.o wait.o
USER_OBJS := $(obj-y)
include arch/um/scripts/Makefile.rules

View File

@@ -0,0 +1,377 @@
/**********************************************************************
proxy.c
Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing
terms and conditions.
Jeff Dike (jdike@karaya.com) : Modified for integration into uml
**********************************************************************/
/* XXX This file shouldn't refer to CONFIG_* */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <termios.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <asm/unistd.h>
#include "ptrace_user.h"
#include "ptproxy.h"
#include "sysdep.h"
#include "wait.h"
#include "user_util.h"
#include "user.h"
#include "os.h"
#include "tempfile.h"
static int debugger_wait(debugger_state *debugger, int *status, int options,
int (*syscall)(debugger_state *debugger, pid_t child),
int (*normal_return)(debugger_state *debugger,
pid_t unused),
int (*wait_return)(debugger_state *debugger,
pid_t unused))
{
if(debugger->real_wait){
debugger->handle_trace = normal_return;
syscall_continue(debugger->pid);
debugger->real_wait = 0;
return(1);
}
debugger->wait_status_ptr = status;
debugger->wait_options = options;
if((debugger->debugee != NULL) && debugger->debugee->event){
syscall_continue(debugger->pid);
wait_for_stop(debugger->pid, SIGTRAP, PTRACE_SYSCALL,
NULL);
(*wait_return)(debugger, -1);
return(0);
}
else if(debugger->wait_options & WNOHANG){
syscall_cancel(debugger->pid, 0);
debugger->handle_trace = syscall;
return(0);
}
else {
syscall_pause(debugger->pid);
debugger->handle_trace = wait_return;
debugger->waiting = 1;
}
return(1);
}
/*
* Handle debugger trap, i.e. syscall.
*/
int debugger_syscall(debugger_state *debugger, pid_t child)
{
long arg1, arg2, arg3, arg4, arg5, result;
int syscall, ret = 0;
syscall = get_syscall(debugger->pid, &arg1, &arg2, &arg3, &arg4,
&arg5);
switch(syscall){
case __NR_execve:
/* execve never returns */
debugger->handle_trace = debugger_syscall;
break;
case __NR_ptrace:
if(debugger->debugee->pid != 0) arg2 = debugger->debugee->pid;
if(!debugger->debugee->in_context)
child = debugger->debugee->pid;
result = proxy_ptrace(debugger, arg1, arg2, arg3, arg4, child,
&ret);
syscall_cancel(debugger->pid, result);
debugger->handle_trace = debugger_syscall;
return(ret);
#ifdef __NR_waitpid
case __NR_waitpid:
#endif
case __NR_wait4:
if(!debugger_wait(debugger, (int *) arg2, arg3,
debugger_syscall, debugger_normal_return,
proxy_wait_return))
return(0);
break;
case __NR_kill:
if(!debugger->debugee->in_context)
child = debugger->debugee->pid;
if(arg1 == debugger->debugee->pid){
result = kill(child, arg2);
syscall_cancel(debugger->pid, result);
debugger->handle_trace = debugger_syscall;
return(0);
}
else debugger->handle_trace = debugger_normal_return;
break;
default:
debugger->handle_trace = debugger_normal_return;
}
syscall_continue(debugger->pid);
return(0);
}
/* Used by the tracing thread */
static debugger_state parent;
static int parent_syscall(debugger_state *debugger, int pid);
int init_parent_proxy(int pid)
{
parent = ((debugger_state) { .pid = pid,
.wait_options = 0,
.wait_status_ptr = NULL,
.waiting = 0,
.real_wait = 0,
.expecting_child = 0,
.handle_trace = parent_syscall,
.debugee = NULL } );
return(0);
}
int parent_normal_return(debugger_state *debugger, pid_t unused)
{
debugger->handle_trace = parent_syscall;
syscall_continue(debugger->pid);
return(0);
}
static int parent_syscall(debugger_state *debugger, int pid)
{
long arg1, arg2, arg3, arg4, arg5;
int syscall;
syscall = get_syscall(pid, &arg1, &arg2, &arg3, &arg4, &arg5);
if((syscall == __NR_wait4)
#ifdef __NR_waitpid
|| (syscall == __NR_waitpid)
#endif
){
debugger_wait(&parent, (int *) arg2, arg3, parent_syscall,
parent_normal_return, parent_wait_return);
}
else ptrace(PTRACE_SYSCALL, pid, 0, 0);
return(0);
}
int debugger_normal_return(debugger_state *debugger, pid_t unused)
{
debugger->handle_trace = debugger_syscall;
syscall_continue(debugger->pid);
return(0);
}
void debugger_cancelled_return(debugger_state *debugger, int result)
{
debugger->handle_trace = debugger_syscall;
syscall_set_result(debugger->pid, result);
syscall_continue(debugger->pid);
}
/* Used by the tracing thread */
static debugger_state debugger;
static debugee_state debugee;
void init_proxy (pid_t debugger_pid, int stopped, int status)
{
debugger.pid = debugger_pid;
debugger.handle_trace = debugger_syscall;
debugger.debugee = &debugee;
debugger.waiting = 0;
debugger.real_wait = 0;
debugger.expecting_child = 0;
debugee.pid = 0;
debugee.traced = 0;
debugee.stopped = stopped;
debugee.event = 0;
debugee.zombie = 0;
debugee.died = 0;
debugee.wait_status = status;
debugee.in_context = 1;
}
int debugger_proxy(int status, int pid)
{
int ret = 0, sig;
if(WIFSTOPPED(status)){
sig = WSTOPSIG(status);
if (sig == SIGTRAP)
ret = (*debugger.handle_trace)(&debugger, pid);
else if(sig == SIGCHLD){
if(debugger.expecting_child){
ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig);
debugger.expecting_child = 0;
}
else if(debugger.waiting)
real_wait_return(&debugger);
else {
ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig);
debugger.real_wait = 1;
}
}
else ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig);
}
else if(WIFEXITED(status)){
tracer_panic("debugger (pid %d) exited with status %d",
debugger.pid, WEXITSTATUS(status));
}
else if(WIFSIGNALED(status)){
tracer_panic("debugger (pid %d) exited with signal %d",
debugger.pid, WTERMSIG(status));
}
else {
tracer_panic("proxy got unknown status (0x%x) on debugger "
"(pid %d)", status, debugger.pid);
}
return(ret);
}
void child_proxy(pid_t pid, int status)
{
debugee.event = 1;
debugee.wait_status = status;
if(WIFSTOPPED(status)){
debugee.stopped = 1;
debugger.expecting_child = 1;
kill(debugger.pid, SIGCHLD);
}
else if(WIFEXITED(status) || WIFSIGNALED(status)){
debugee.zombie = 1;
debugger.expecting_child = 1;
kill(debugger.pid, SIGCHLD);
}
else panic("proxy got unknown status (0x%x) on child (pid %d)",
status, pid);
}
void debugger_parent_signal(int status, int pid)
{
int sig;
if(WIFSTOPPED(status)){
sig = WSTOPSIG(status);
if(sig == SIGTRAP) (*parent.handle_trace)(&parent, pid);
else ptrace(PTRACE_SYSCALL, pid, 0, sig);
}
}
void fake_child_exit(void)
{
int status, pid;
child_proxy(1, W_EXITCODE(0, 0));
while(debugger.waiting == 1){
CATCH_EINTR(pid = waitpid(debugger.pid, &status, WUNTRACED));
if(pid != debugger.pid){
printk("fake_child_exit - waitpid failed, "
"errno = %d\n", errno);
return;
}
debugger_proxy(status, debugger.pid);
}
CATCH_EINTR(pid = waitpid(debugger.pid, &status, WUNTRACED));
if(pid != debugger.pid){
printk("fake_child_exit - waitpid failed, "
"errno = %d\n", errno);
return;
}
if(ptrace(PTRACE_DETACH, debugger.pid, 0, SIGCONT) < 0)
printk("fake_child_exit - PTRACE_DETACH failed, errno = %d\n",
errno);
}
char gdb_init_string[] =
"att 1 \n\
b panic \n\
b stop \n\
handle SIGWINCH nostop noprint pass \n\
";
int start_debugger(char *prog, int startup, int stop, int *fd_out)
{
int slave, child;
slave = open_gdb_chan();
child = fork();
if(child == 0){
char *tempname = NULL;
int fd;
if(setsid() < 0) perror("setsid");
if((dup2(slave, 0) < 0) || (dup2(slave, 1) < 0) ||
(dup2(slave, 2) < 0)){
printk("start_debugger : dup2 failed, errno = %d\n",
errno);
exit(1);
}
if(ioctl(0, TIOCSCTTY, 0) < 0){
printk("start_debugger : TIOCSCTTY failed, "
"errno = %d\n", errno);
exit(1);
}
if(tcsetpgrp (1, os_getpid()) < 0){
printk("start_debugger : tcsetpgrp failed, "
"errno = %d\n", errno);
#ifdef notdef
exit(1);
#endif
}
fd = make_tempfile("/tmp/gdb_init-XXXXXX", &tempname, 0);
if(fd < 0){
printk("start_debugger : make_tempfile failed,"
"err = %d\n", -fd);
exit(1);
}
os_write_file(fd, gdb_init_string, sizeof(gdb_init_string) - 1);
if(startup){
if(stop){
os_write_file(fd, "b start_kernel\n",
strlen("b start_kernel\n"));
}
os_write_file(fd, "c\n", strlen("c\n"));
}
if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){
printk("start_debugger : PTRACE_TRACEME failed, "
"errno = %d\n", errno);
exit(1);
}
execlp("gdb", "gdb", "--command", tempname, prog, NULL);
printk("start_debugger : exec of gdb failed, errno = %d\n",
errno);
}
if(child < 0){
printk("start_debugger : fork for gdb failed, errno = %d\n",
errno);
return(-1);
}
*fd_out = slave;
return(child);
}
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

View File

@@ -0,0 +1,61 @@
/**********************************************************************
ptproxy.h
Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing
terms and conditions.
**********************************************************************/
#ifndef __PTPROXY_H
#define __PTPROXY_H
#include <sys/types.h>
typedef struct debugger debugger_state;
typedef struct debugee debugee_state;
struct debugger
{
pid_t pid;
int wait_options;
int *wait_status_ptr;
unsigned int waiting : 1;
unsigned int real_wait : 1;
unsigned int expecting_child : 1;
int (*handle_trace) (debugger_state *, pid_t);
debugee_state *debugee;
};
struct debugee
{
pid_t pid;
int wait_status;
unsigned int died : 1;
unsigned int event : 1;
unsigned int stopped : 1;
unsigned int trace_singlestep : 1;
unsigned int trace_syscall : 1;
unsigned int traced : 1;
unsigned int zombie : 1;
unsigned int in_context : 1;
};
extern int debugger_syscall(debugger_state *debugger, pid_t pid);
extern int debugger_normal_return (debugger_state *debugger, pid_t unused);
extern long proxy_ptrace (struct debugger *, int, pid_t, long, long, pid_t,
int *strace_out);
extern void debugger_cancelled_return(debugger_state *debugger, int result);
#endif
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

View File

@@ -0,0 +1,238 @@
/**********************************************************************
ptrace.c
Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing
terms and conditions.
Jeff Dike (jdike@karaya.com) : Modified for integration into uml
**********************************************************************/
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/wait.h>
#include "ptproxy.h"
#include "debug.h"
#include "user_util.h"
#include "kern_util.h"
#include "ptrace_user.h"
#include "tt.h"
#include "os.h"
long proxy_ptrace(struct debugger *debugger, int arg1, pid_t arg2,
long arg3, long arg4, pid_t child, int *ret)
{
sigset_t relay;
long result;
int status;
*ret = 0;
if(debugger->debugee->died) return(-ESRCH);
switch(arg1){
case PTRACE_ATTACH:
if(debugger->debugee->traced) return(-EPERM);
debugger->debugee->pid = arg2;
debugger->debugee->traced = 1;
if(is_valid_pid(arg2) && (arg2 != child)){
debugger->debugee->in_context = 0;
kill(arg2, SIGSTOP);
debugger->debugee->event = 1;
debugger->debugee->wait_status = W_STOPCODE(SIGSTOP);
}
else {
debugger->debugee->in_context = 1;
if(debugger->debugee->stopped)
child_proxy(child, W_STOPCODE(SIGSTOP));
else kill(child, SIGSTOP);
}
return(0);
case PTRACE_DETACH:
if(!debugger->debugee->traced) return(-EPERM);
debugger->debugee->traced = 0;
debugger->debugee->pid = 0;
if(!debugger->debugee->in_context)
kill(child, SIGCONT);
return(0);
case PTRACE_CONT:
if(!debugger->debugee->in_context) return(-EPERM);
*ret = PTRACE_CONT;
return(ptrace(PTRACE_CONT, child, arg3, arg4));
#ifdef UM_HAVE_GETFPREGS
case PTRACE_GETFPREGS:
{
long regs[FP_FRAME_SIZE];
int i, result;
result = ptrace(PTRACE_GETFPREGS, child, 0, regs);
if(result == -1) return(-errno);
for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
ptrace(PTRACE_POKEDATA, debugger->pid, arg4 + 4 * i,
regs[i]);
return(result);
}
#endif
#ifdef UM_HAVE_GETFPXREGS
case PTRACE_GETFPXREGS:
{
long regs[FPX_FRAME_SIZE];
int i, result;
result = ptrace(PTRACE_GETFPXREGS, child, 0, regs);
if(result == -1) return(-errno);
for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
ptrace(PTRACE_POKEDATA, debugger->pid, arg4 + 4 * i,
regs[i]);
return(result);
}
#endif
#ifdef UM_HAVE_GETREGS
case PTRACE_GETREGS:
{
long regs[FRAME_SIZE];
int i, result;
result = ptrace(PTRACE_GETREGS, child, 0, regs);
if(result == -1) return(-errno);
for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
ptrace (PTRACE_POKEDATA, debugger->pid,
arg4 + 4 * i, regs[i]);
return(result);
}
break;
#endif
case PTRACE_KILL:
result = ptrace(PTRACE_KILL, child, arg3, arg4);
if(result == -1) return(-errno);
return(result);
case PTRACE_PEEKDATA:
case PTRACE_PEEKTEXT:
case PTRACE_PEEKUSR:
/* The value being read out could be -1, so we have to
* check errno to see if there's an error, and zero it
* beforehand so we're not faked out by an old error
*/
errno = 0;
result = ptrace(arg1, child, arg3, 0);
if((result == -1) && (errno != 0)) return(-errno);
result = ptrace(PTRACE_POKEDATA, debugger->pid, arg4, result);
if(result == -1) return(-errno);
return(result);
case PTRACE_POKEDATA:
case PTRACE_POKETEXT:
case PTRACE_POKEUSR:
result = ptrace(arg1, child, arg3, arg4);
if(result == -1) return(-errno);
if(arg1 == PTRACE_POKEUSR) ptrace_pokeuser(arg3, arg4);
return(result);
#ifdef UM_HAVE_SETFPREGS
case PTRACE_SETFPREGS:
{
long regs[FP_FRAME_SIZE];
int i;
for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
regs[i] = ptrace (PTRACE_PEEKDATA, debugger->pid,
arg4 + 4 * i, 0);
result = ptrace(PTRACE_SETFPREGS, child, 0, regs);
if(result == -1) return(-errno);
return(result);
}
#endif
#ifdef UM_HAVE_SETFPXREGS
case PTRACE_SETFPXREGS:
{
long regs[FPX_FRAME_SIZE];
int i;
for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
regs[i] = ptrace (PTRACE_PEEKDATA, debugger->pid,
arg4 + 4 * i, 0);
result = ptrace(PTRACE_SETFPXREGS, child, 0, regs);
if(result == -1) return(-errno);
return(result);
}
#endif
#ifdef UM_HAVE_SETREGS
case PTRACE_SETREGS:
{
long regs[FRAME_SIZE];
int i;
for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++)
regs[i] = ptrace(PTRACE_PEEKDATA, debugger->pid,
arg4 + 4 * i, 0);
result = ptrace(PTRACE_SETREGS, child, 0, regs);
if(result == -1) return(-errno);
return(result);
}
#endif
case PTRACE_SINGLESTEP:
if(!debugger->debugee->in_context) return(-EPERM);
sigemptyset(&relay);
sigaddset(&relay, SIGSEGV);
sigaddset(&relay, SIGILL);
sigaddset(&relay, SIGBUS);
result = ptrace(PTRACE_SINGLESTEP, child, arg3, arg4);
if(result == -1) return(-errno);
status = wait_for_stop(child, SIGTRAP, PTRACE_SINGLESTEP,
&relay);
child_proxy(child, status);
return(result);
case PTRACE_SYSCALL:
if(!debugger->debugee->in_context) return(-EPERM);
result = ptrace(PTRACE_SYSCALL, child, arg3, arg4);
if(result == -1) return(-errno);
*ret = PTRACE_SYSCALL;
return(result);
case PTRACE_TRACEME:
default:
return(-EINVAL);
}
}
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

View File

@@ -0,0 +1,71 @@
/**********************************************************************
sysdep.c
Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing
terms and conditions.
**********************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#include <linux/unistd.h>
#include "ptrace_user.h"
#include "user_util.h"
#include "user.h"
#include "os.h"
int get_syscall(pid_t pid, long *arg1, long *arg2, long *arg3, long *arg4,
long *arg5)
{
*arg1 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG1_OFFSET, 0);
*arg2 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG2_OFFSET, 0);
*arg3 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG3_OFFSET, 0);
*arg4 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG4_OFFSET, 0);
*arg5 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG5_OFFSET, 0);
return(ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_NR_OFFSET, 0));
}
void syscall_cancel(pid_t pid, int result)
{
if((ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET,
__NR_getpid) < 0) ||
(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) ||
(wait_for_stop(pid, SIGTRAP, PTRACE_SYSCALL, NULL) < 0) ||
(ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET, result) < 0) ||
(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0))
printk("ptproxy: couldn't cancel syscall: errno = %d\n",
errno);
}
void syscall_set_result(pid_t pid, long result)
{
ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET, result);
}
void syscall_continue(pid_t pid)
{
ptrace(PTRACE_SYSCALL, pid, 0, 0);
}
int syscall_pause(pid_t pid)
{
if(ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET, __NR_pause) < 0){
printk("syscall_change - ptrace failed, errno = %d\n", errno);
return(-1);
}
return(0);
}
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

View File

@@ -0,0 +1,25 @@
/**********************************************************************
sysdep.h
Copyright (C) 1999 Lars Brinkhoff.
Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
See the file COPYING for licensing terms and conditions.
**********************************************************************/
extern int get_syscall(pid_t pid, long *arg1, long *arg2, long *arg3,
long *arg4, long *arg5);
extern void syscall_cancel (pid_t pid, long result);
extern void syscall_set_result (pid_t pid, long result);
extern void syscall_continue (pid_t pid);
extern int syscall_pause(pid_t pid);
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

View File

@@ -0,0 +1,86 @@
/**********************************************************************
wait.c
Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing
terms and conditions.
**********************************************************************/
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include "ptproxy.h"
#include "sysdep.h"
#include "wait.h"
#include "user_util.h"
#include "ptrace_user.h"
#include "sysdep/ptrace.h"
#include "sysdep/sigcontext.h"
int proxy_wait_return(struct debugger *debugger, pid_t unused)
{
debugger->waiting = 0;
if(debugger->debugee->died || (debugger->wait_options & __WCLONE)){
debugger_cancelled_return(debugger, -ECHILD);
return(0);
}
if(debugger->debugee->zombie && debugger->debugee->event)
debugger->debugee->died = 1;
if(debugger->debugee->event){
debugger->debugee->event = 0;
ptrace(PTRACE_POKEDATA, debugger->pid,
debugger->wait_status_ptr,
debugger->debugee->wait_status);
/* if (wait4)
ptrace (PTRACE_POKEDATA, pid, rusage_ptr, ...); */
debugger_cancelled_return(debugger, debugger->debugee->pid);
return(0);
}
/* pause will return -EINTR, which happens to be right for wait */
debugger_normal_return(debugger, -1);
return(0);
}
int parent_wait_return(struct debugger *debugger, pid_t unused)
{
return(debugger_normal_return(debugger, -1));
}
int real_wait_return(struct debugger *debugger)
{
unsigned long ip;
int pid;
pid = debugger->pid;
ip = ptrace(PTRACE_PEEKUSR, pid, PT_IP_OFFSET, 0);
IP_RESTART_SYSCALL(ip);
if(ptrace(PTRACE_POKEUSR, pid, PT_IP_OFFSET, ip) < 0)
tracer_panic("real_wait_return : Failed to restart system "
"call, errno = %d\n", errno);
if((ptrace(PTRACE_SYSCALL, debugger->pid, 0, SIGCHLD) < 0) ||
(ptrace(PTRACE_SYSCALL, debugger->pid, 0, 0) < 0) ||
(ptrace(PTRACE_SYSCALL, debugger->pid, 0, 0) < 0) ||
debugger_normal_return(debugger, -1))
tracer_panic("real_wait_return : gdb failed to wait, "
"errno = %d\n", errno);
return(0);
}
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

View File

@@ -0,0 +1,15 @@
/**********************************************************************
wait.h
Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing
terms and conditions.
**********************************************************************/
#ifndef __PTPROXY_WAIT_H
#define __PTPROXY_WAIT_H
extern int proxy_wait_return(struct debugger *debugger, pid_t unused);
extern int real_wait_return(struct debugger *debugger);
extern int parent_wait_return(struct debugger *debugger, pid_t unused);
#endif

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
* Licensed under the GPL
*/
#include "linux/types.h"
#include "linux/utime.h"
#include "linux/sys.h"
#include "linux/ptrace.h"
#include "asm/unistd.h"
#include "asm/ptrace.h"
#include "asm/uaccess.h"
#include "asm/stat.h"
#include "sysdep/syscalls.h"
#include "sysdep/sigcontext.h"
#include "kern_util.h"
#include "syscall.h"
void syscall_handler_tt(int sig, struct pt_regs *regs)
{
void *sc;
long result;
int syscall;
sc = UPT_SC(&regs->regs);
SC_START_SYSCALL(sc);
syscall = UPT_SYSCALL_NR(&regs->regs);
syscall_trace(&regs->regs, 0);
current->thread.nsyscalls++;
nsyscalls++;
if((syscall >= NR_syscalls) || (syscall < 0))
result = -ENOSYS;
else result = EXECUTE_SYSCALL(syscall, regs);
/* regs->sc may have changed while the system call ran (there may
* have been an interrupt or segfault), so it needs to be refreshed.
*/
UPT_SC(&regs->regs) = sc;
SC_SET_SYSCALL_RETURN(sc, result);
syscall_trace(&regs->regs, 1);
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <asm/unistd.h>
#include "sysdep/ptrace.h"
#include "sigcontext.h"
#include "ptrace_user.h"
#include "task.h"
#include "user_util.h"
#include "kern_util.h"
#include "syscall.h"
#include "tt.h"
void do_sigtrap(void *task)
{
UPT_SYSCALL_NR(TASK_REGS(task)) = -1;
}
void do_syscall(void *task, int pid, int local_using_sysemu)
{
unsigned long proc_regs[FRAME_SIZE];
if(ptrace_getregs(pid, proc_regs) < 0)
tracer_panic("Couldn't read registers");
UPT_SYSCALL_NR(TASK_REGS(task)) = PT_SYSCALL_NR(proc_regs);
#ifdef UPT_ORIGGPR2
UPT_ORIGGPR2(TASK_REGS(task)) = REGS_ORIGGPR2(proc_regs);
#endif
if(((unsigned long *) PT_IP(proc_regs) >= &_stext) &&
((unsigned long *) PT_IP(proc_regs) <= &_etext))
tracer_panic("I'm tracing myself and I can't get out");
/* advanced sysemu mode set syscall number to -1 automatically */
if (local_using_sysemu==2)
return;
/* syscall number -1 in sysemu skips syscall restarting in host */
if(ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET,
local_using_sysemu ? -1 : __NR_getpid) < 0)
tracer_panic("do_syscall : Nullifying syscall failed, "
"errno = %d", errno);
}
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

121
arch/um/kernel/tt/tlb.c Normal file
View File

@@ -0,0 +1,121 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Copyright 2003 PathScale, Inc.
* Licensed under the GPL
*/
#include "linux/stddef.h"
#include "linux/kernel.h"
#include "linux/sched.h"
#include "linux/mm.h"
#include "asm/page.h"
#include "asm/pgtable.h"
#include "asm/uaccess.h"
#include "asm/tlbflush.h"
#include "user_util.h"
#include "mem_user.h"
#include "os.h"
#include "tlb.h"
static int do_ops(union mm_context *mmu, struct host_vm_op *ops, int last,
int finished, void **flush)
{
struct host_vm_op *op;
int i, ret=0;
for(i = 0; i <= last && !ret; i++){
op = &ops[i];
switch(op->type){
case MMAP:
ret = os_map_memory((void *) op->u.mmap.addr,
op->u.mmap.fd, op->u.mmap.offset,
op->u.mmap.len, op->u.mmap.r,
op->u.mmap.w, op->u.mmap.x);
break;
case MUNMAP:
ret = os_unmap_memory((void *) op->u.munmap.addr,
op->u.munmap.len);
break;
case MPROTECT:
ret = protect_memory(op->u.mprotect.addr,
op->u.munmap.len,
op->u.mprotect.r,
op->u.mprotect.w,
op->u.mprotect.x, 1);
protect_memory(op->u.mprotect.addr, op->u.munmap.len,
op->u.mprotect.r, op->u.mprotect.w,
op->u.mprotect.x, 1);
break;
default:
printk("Unknown op type %d in do_ops\n", op->type);
break;
}
}
return ret;
}
static void fix_range(struct mm_struct *mm, unsigned long start_addr,
unsigned long end_addr, int force)
{
if((current->thread.mode.tt.extern_pid != -1) &&
(current->thread.mode.tt.extern_pid != os_getpid()))
panic("fix_range fixing wrong address space, current = 0x%p",
current);
fix_range_common(mm, start_addr, end_addr, force, do_ops);
}
atomic_t vmchange_seq = ATOMIC_INIT(1);
void flush_tlb_kernel_range_tt(unsigned long start, unsigned long end)
{
if(flush_tlb_kernel_range_common(start, end))
atomic_inc(&vmchange_seq);
}
void flush_tlb_kernel_vm_tt(void)
{
flush_tlb_kernel_range(start_vm, end_vm);
}
void __flush_tlb_one_tt(unsigned long addr)
{
flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
}
void flush_tlb_range_tt(struct vm_area_struct *vma, unsigned long start,
unsigned long end)
{
if(vma->vm_mm != current->mm) return;
/* Assumes that the range start ... end is entirely within
* either process memory or kernel vm
*/
if((start >= start_vm) && (start < end_vm)){
if(flush_tlb_kernel_range_common(start, end))
atomic_inc(&vmchange_seq);
}
else fix_range(vma->vm_mm, start, end, 0);
}
void flush_tlb_mm_tt(struct mm_struct *mm)
{
unsigned long seq;
if(mm != current->mm) return;
fix_range(mm, 0, STACK_TOP, 0);
seq = atomic_read(&vmchange_seq);
if(current->thread.mode.tt.vm_seq == seq)
return;
current->thread.mode.tt.vm_seq = seq;
flush_tlb_kernel_range_common(start_vm, end_vm);
}
void force_flush_all_tt(void)
{
fix_range(current->mm, 0, STACK_TOP, 1);
flush_tlb_kernel_range_common(start_vm, end_vm);
}

462
arch/um/kernel/tt/tracer.c Normal file
View File

@@ -0,0 +1,462 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sched.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/wait.h>
#include "user.h"
#include "sysdep/ptrace.h"
#include "sigcontext.h"
#include "sysdep/sigcontext.h"
#include "os.h"
#include "user_util.h"
#include "mem_user.h"
#include "process.h"
#include "kern_util.h"
#include "chan_user.h"
#include "ptrace_user.h"
#include "irq_user.h"
#include "mode.h"
#include "tt.h"
static int tracer_winch[2];
int is_tracer_winch(int pid, int fd, void *data)
{
if(pid != os_getpgrp())
return(0);
register_winch_irq(tracer_winch[0], fd, -1, data);
return(1);
}
static void tracer_winch_handler(int sig)
{
int n;
char c = 1;
n = os_write_file(tracer_winch[1], &c, sizeof(c));
if(n != sizeof(c))
printk("tracer_winch_handler - write failed, err = %d\n", -n);
}
/* Called only by the tracing thread during initialization */
static void setup_tracer_winch(void)
{
int err;
err = os_pipe(tracer_winch, 1, 1);
if(err < 0){
printk("setup_tracer_winch : os_pipe failed, err = %d\n", -err);
return;
}
signal(SIGWINCH, tracer_winch_handler);
}
void attach_process(int pid)
{
if((ptrace(PTRACE_ATTACH, pid, 0, 0) < 0) ||
(ptrace(PTRACE_CONT, pid, 0, 0) < 0))
tracer_panic("OP_FORK failed to attach pid");
wait_for_stop(pid, SIGSTOP, PTRACE_CONT, NULL);
if (ptrace(PTRACE_OLDSETOPTIONS, pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0)
tracer_panic("OP_FORK: PTRACE_SETOPTIONS failed, errno = %d", errno);
if(ptrace(PTRACE_CONT, pid, 0, 0) < 0)
tracer_panic("OP_FORK failed to continue process");
}
void tracer_panic(char *format, ...)
{
va_list ap;
va_start(ap, format);
vprintf(format, ap);
va_end(ap);
printf("\n");
while(1) pause();
}
static void tracer_segv(int sig, struct sigcontext sc)
{
struct faultinfo fi;
GET_FAULTINFO_FROM_SC(fi, &sc);
printf("Tracing thread segfault at address 0x%lx, ip 0x%lx\n",
FAULT_ADDRESS(fi), SC_IP(&sc));
while(1)
pause();
}
/* Changed early in boot, and then only read */
int debug = 0;
int debug_stop = 1;
int debug_parent = 0;
int honeypot = 0;
static int signal_tramp(void *arg)
{
int (*proc)(void *);
if(honeypot && munmap((void *) (host_task_size - 0x10000000),
0x10000000))
panic("Unmapping stack failed");
if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0)
panic("ptrace PTRACE_TRACEME failed");
os_stop_process(os_getpid());
change_sig(SIGWINCH, 0);
signal(SIGUSR1, SIG_IGN);
change_sig(SIGCHLD, 0);
signal(SIGSEGV, (__sighandler_t) sig_handler);
set_cmdline("(idle thread)");
set_init_pid(os_getpid());
init_irq_signals(0);
proc = arg;
return((*proc)(NULL));
}
static void sleeping_process_signal(int pid, int sig)
{
switch(sig){
/* These two result from UML being ^Z-ed and bg-ed. PTRACE_CONT is
* right because the process must be in the kernel already.
*/
case SIGCONT:
case SIGTSTP:
if(ptrace(PTRACE_CONT, pid, 0, sig) < 0)
tracer_panic("sleeping_process_signal : Failed to "
"continue pid %d, signal = %d, "
"errno = %d\n", pid, sig, errno);
break;
/* This happens when the debugger (e.g. strace) is doing system call
* tracing on the kernel. During a context switch, the current task
* will be set to the incoming process and the outgoing process will
* hop into write and then read. Since it's not the current process
* any more, the trace of those will land here. So, we need to just
* PTRACE_SYSCALL it.
*/
case (SIGTRAP + 0x80):
if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
tracer_panic("sleeping_process_signal : Failed to "
"PTRACE_SYSCALL pid %d, errno = %d\n",
pid, errno);
break;
case SIGSTOP:
break;
default:
tracer_panic("sleeping process %d got unexpected "
"signal : %d\n", pid, sig);
break;
}
}
/* Accessed only by the tracing thread */
int debugger_pid = -1;
int debugger_parent = -1;
int debugger_fd = -1;
int gdb_pid = -1;
struct {
int pid;
int signal;
unsigned long addr;
struct timeval time;
} signal_record[1024][32];
int signal_index[32];
int nsignals = 0;
int debug_trace = 0;
extern void signal_usr1(int sig);
int tracing_pid = -1;
int tracer(int (*init_proc)(void *), void *sp)
{
void *task = NULL;
int status, pid = 0, sig = 0, cont_type, tracing = 0, op = 0;
int proc_id = 0, n, err, old_tracing = 0, strace = 0;
int local_using_sysemu = 0;
signal(SIGPIPE, SIG_IGN);
setup_tracer_winch();
tracing_pid = os_getpid();
printf("tracing thread pid = %d\n", tracing_pid);
pid = clone(signal_tramp, sp, CLONE_FILES | SIGCHLD, init_proc);
CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
if(n < 0){
printf("waitpid on idle thread failed, errno = %d\n", errno);
exit(1);
}
if (ptrace(PTRACE_OLDSETOPTIONS, pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0) {
printf("Failed to PTRACE_SETOPTIONS for idle thread, errno = %d\n", errno);
exit(1);
}
if((ptrace(PTRACE_CONT, pid, 0, 0) < 0)){
printf("Failed to continue idle thread, errno = %d\n", errno);
exit(1);
}
signal(SIGSEGV, (sighandler_t) tracer_segv);
signal(SIGUSR1, signal_usr1);
if(debug_trace){
printf("Tracing thread pausing to be attached\n");
stop();
}
if(debug){
if(gdb_pid != -1)
debugger_pid = attach_debugger(pid, gdb_pid, 1);
else debugger_pid = init_ptrace_proxy(pid, 1, debug_stop);
if(debug_parent){
debugger_parent = os_process_parent(debugger_pid);
init_parent_proxy(debugger_parent);
err = attach(debugger_parent);
if(err){
printf("Failed to attach debugger parent %d, "
"errno = %d\n", debugger_parent, -err);
debugger_parent = -1;
}
else {
if(ptrace(PTRACE_SYSCALL, debugger_parent,
0, 0) < 0){
printf("Failed to continue debugger "
"parent, errno = %d\n", errno);
debugger_parent = -1;
}
}
}
}
set_cmdline("(tracing thread)");
while(1){
CATCH_EINTR(pid = waitpid(-1, &status, WUNTRACED));
if(pid <= 0){
if(errno != ECHILD){
printf("wait failed - errno = %d\n", errno);
}
continue;
}
if(pid == debugger_pid){
int cont = 0;
if(WIFEXITED(status) || WIFSIGNALED(status))
debugger_pid = -1;
/* XXX Figure out how to deal with gdb and SMP */
else cont = debugger_signal(status, cpu_tasks[0].pid);
if(cont == PTRACE_SYSCALL) strace = 1;
continue;
}
else if(pid == debugger_parent){
debugger_parent_signal(status, pid);
continue;
}
nsignals++;
if(WIFEXITED(status)) ;
#ifdef notdef
{
printf("Child %d exited with status %d\n", pid,
WEXITSTATUS(status));
}
#endif
else if(WIFSIGNALED(status)){
sig = WTERMSIG(status);
if(sig != 9){
printf("Child %d exited with signal %d\n", pid,
sig);
}
}
else if(WIFSTOPPED(status)){
proc_id = pid_to_processor_id(pid);
sig = WSTOPSIG(status);
if(proc_id == -1){
sleeping_process_signal(pid, sig);
continue;
}
task = cpu_tasks[proc_id].task;
tracing = is_tracing(task);
old_tracing = tracing;
/* Assume: no syscall, when coming from user */
if ( tracing )
do_sigtrap(task);
switch(sig){
case SIGUSR1:
sig = 0;
op = do_proc_op(task, proc_id);
switch(op){
/*
* This is called when entering user mode; after
* this, we start intercepting syscalls.
*
* In fact, a process is started in kernel mode,
* so with is_tracing() == 0 (and that is reset
* when executing syscalls, since UML kernel has
* the right to do syscalls);
*/
case OP_TRACE_ON:
arch_leave_kernel(task, pid);
tracing = 1;
break;
case OP_REBOOT:
case OP_HALT:
unmap_physmem();
kmalloc_ok = 0;
os_kill_ptraced_process(pid, 0);
/* Now let's reap remaining zombies */
errno = 0;
do {
waitpid(-1, &status,
WUNTRACED);
} while (errno != ECHILD);
return(op == OP_REBOOT);
case OP_NONE:
printf("Detaching pid %d\n", pid);
detach(pid, SIGSTOP);
continue;
default:
break;
}
/* OP_EXEC switches host processes on us,
* we want to continue the new one.
*/
pid = cpu_tasks[proc_id].pid;
break;
case (SIGTRAP + 0x80):
if(!tracing && (debugger_pid != -1)){
child_signal(pid, status & 0x7fff);
continue;
}
tracing = 0;
/* local_using_sysemu has been already set
* below, since if we are here, is_tracing() on
* the traced task was 1, i.e. the process had
* already run through one iteration of the
* loop which executed a OP_TRACE_ON request.*/
do_syscall(task, pid, local_using_sysemu);
sig = SIGUSR2;
break;
case SIGTRAP:
if(!tracing && (debugger_pid != -1)){
child_signal(pid, status);
continue;
}
tracing = 0;
break;
case SIGPROF:
if(tracing) sig = 0;
break;
case SIGCHLD:
case SIGHUP:
sig = 0;
break;
case SIGSEGV:
case SIGIO:
case SIGALRM:
case SIGVTALRM:
case SIGFPE:
case SIGBUS:
case SIGILL:
case SIGWINCH:
default:
tracing = 0;
break;
}
set_tracing(task, tracing);
if(!tracing && old_tracing)
arch_enter_kernel(task, pid);
if(!tracing && (debugger_pid != -1) && (sig != 0) &&
(sig != SIGALRM) && (sig != SIGVTALRM) &&
(sig != SIGSEGV) && (sig != SIGTRAP) &&
(sig != SIGUSR2) && (sig != SIGIO) &&
(sig != SIGFPE)){
child_signal(pid, status);
continue;
}
local_using_sysemu = get_using_sysemu();
if(tracing)
cont_type = SELECT_PTRACE_OPERATION(local_using_sysemu,
singlestepping(task));
else if((debugger_pid != -1) && strace)
cont_type = PTRACE_SYSCALL;
else
cont_type = PTRACE_CONT;
if(ptrace(cont_type, pid, 0, sig) != 0){
tracer_panic("ptrace failed to continue "
"process - errno = %d\n",
errno);
}
}
}
return(0);
}
static int __init uml_debug_setup(char *line, int *add)
{
char *next;
debug = 1;
*add = 0;
if(*line != '=') return(0);
line++;
while(line != NULL){
next = strchr(line, ',');
if(next) *next++ = '\0';
if(!strcmp(line, "go")) debug_stop = 0;
else if(!strcmp(line, "parent")) debug_parent = 1;
else printf("Unknown debug option : '%s'\n", line);
line = next;
}
return(0);
}
__uml_setup("debug", uml_debug_setup,
"debug\n"
" Starts up the kernel under the control of gdb. See the \n"
" kernel debugging tutorial and the debugging session pages\n"
" at http://user-mode-linux.sourceforge.net/ for more information.\n\n"
);
static int __init uml_debugtrace_setup(char *line, int *add)
{
debug_trace = 1;
return 0;
}
__uml_setup("debugtrace", uml_debugtrace_setup,
"debugtrace\n"
" Causes the tracing thread to pause until it is attached by a\n"
" debugger and continued. This is mostly for debugging crashes\n"
" early during boot, and should be pretty much obsoleted by\n"
" the debug switch.\n\n"
);
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include "sysdep/ptrace.h"
#include "sysdep/sigcontext.h"
#include "user_util.h"
#include "kern_util.h"
#include "task.h"
#include "tt.h"
#include "os.h"
void sig_handler_common_tt(int sig, void *sc_ptr)
{
struct sigcontext *sc = sc_ptr;
struct tt_regs save_regs, *r;
int save_errno = errno, is_user = 0;
void (*handler)(int, union uml_pt_regs *);
/* This is done because to allow SIGSEGV to be delivered inside a SEGV
* handler. This can happen in copy_user, and if SEGV is disabled,
* the process will die.
*/
if(sig == SIGSEGV)
change_sig(SIGSEGV, 1);
r = &TASK_REGS(get_current())->tt;
if ( sig == SIGFPE || sig == SIGSEGV ||
sig == SIGBUS || sig == SIGILL ||
sig == SIGTRAP ) {
GET_FAULTINFO_FROM_SC(r->faultinfo, sc);
}
save_regs = *r;
if (sc)
is_user = user_context(SC_SP(sc));
r->sc = sc;
if(sig != SIGUSR2)
r->syscall = -1;
handler = sig_info[sig];
/* unblock SIGALRM, SIGVTALRM, SIGIO if sig isn't IRQ signal */
if (sig != SIGIO && sig != SIGWINCH &&
sig != SIGVTALRM && sig != SIGALRM)
unblock_signals();
handler(sig, (union uml_pt_regs *) r);
if(is_user){
interrupt_end();
block_signals();
set_user_mode(NULL);
}
*r = save_regs;
errno = save_errno;
}
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
* Licensed under the GPL
*/
#include "linux/sched.h"
#include "asm/uaccess.h"
int copy_from_user_tt(void *to, const void __user *from, int n)
{
if(!access_ok(VERIFY_READ, from, n))
return(n);
return(__do_copy_from_user(to, from, n, &current->thread.fault_addr,
&current->thread.fault_catcher));
}
int copy_to_user_tt(void __user *to, const void *from, int n)
{
if(!access_ok(VERIFY_WRITE, to, n))
return(n);
return(__do_copy_to_user(to, from, n, &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(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(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:
*/

View File

@@ -0,0 +1,106 @@
/*
* Copyright (C) 2001 Chris Emerson (cemerson@chiark.greenend.org.uk)
* Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include <string.h>
#include "user_util.h"
#include "uml_uaccess.h"
#include "task.h"
#include "kern_util.h"
#include "os.h"
#include "longjmp.h"
int __do_copy_from_user(void *to, const void *from, int n,
void **fault_addr, void **fault_catcher)
{
struct tt_regs save = TASK_REGS(get_current())->tt;
unsigned long fault;
int faulted;
fault = __do_user_copy(to, from, n, fault_addr, fault_catcher,
__do_copy, &faulted);
TASK_REGS(get_current())->tt = save;
if(!faulted)
return 0;
else if (fault)
return n - (fault - (unsigned long) from);
else
/* In case of a general protection fault, we don't have the
* fault address, so NULL is used instead. Pretend we didn't
* copy anything. */
return n;
}
static void __do_strncpy(void *dst, const void *src, int count)
{
strncpy(dst, src, count);
}
int __do_strncpy_from_user(char *dst, const char *src, unsigned long count,
void **fault_addr, void **fault_catcher)
{
struct tt_regs save = TASK_REGS(get_current())->tt;
unsigned long fault;
int faulted;
fault = __do_user_copy(dst, src, count, fault_addr, fault_catcher,
__do_strncpy, &faulted);
TASK_REGS(get_current())->tt = save;
if(!faulted) return(strlen(dst));
else return(-1);
}
static void __do_clear(void *to, const void *from, int n)
{
memset(to, 0, n);
}
int __do_clear_user(void *mem, unsigned long len,
void **fault_addr, void **fault_catcher)
{
struct tt_regs save = TASK_REGS(get_current())->tt;
unsigned long fault;
int faulted;
fault = __do_user_copy(mem, NULL, len, fault_addr, fault_catcher,
__do_clear, &faulted);
TASK_REGS(get_current())->tt = save;
if(!faulted) return(0);
else return(len - (fault - (unsigned long) mem));
}
int __do_strnlen_user(const char *str, unsigned long n,
void **fault_addr, void **fault_catcher)
{
struct tt_regs save = TASK_REGS(get_current())->tt;
int ret;
unsigned long *faddrp = (unsigned long *)fault_addr;
jmp_buf jbuf;
*fault_catcher = &jbuf;
if(UML_SETJMP(&jbuf) == 0)
ret = strlen(str) + 1;
else ret = *faddrp - (unsigned long) str;
*fault_addr = NULL;
*fault_catcher = NULL;
TASK_REGS(get_current())->tt = save;
return ret;
}
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/

30
arch/um/kernel/uaccess.c Normal file
View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2001 Chris Emerson (cemerson@chiark.greenend.org.uk)
* Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
/* These are here rather than tt/uaccess.c because skas mode needs them in
* order to do SIGBUS recovery when a tmpfs mount runs out of room.
*/
#include <linux/string.h>
#include "os.h"
void __do_copy(void *to, const void *from, int n)
{
memcpy(to, from, n);
}
int __do_copy_to_user(void *to, const void *from, int n,
void **fault_addr, void **fault_catcher)
{
unsigned long fault;
int faulted;
fault = __do_user_copy(to, from, n, fault_addr, fault_catcher,
__do_copy, &faulted);
if(!faulted) return(0);
else return(n - (fault - (unsigned long) to));
}

509
arch/um/kernel/um_arch.c Normal file
View File

@@ -0,0 +1,509 @@
/*
* Copyright (C) 2000, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "linux/kernel.h"
#include "linux/sched.h"
#include "linux/notifier.h"
#include "linux/mm.h"
#include "linux/types.h"
#include "linux/tty.h"
#include "linux/init.h"
#include "linux/bootmem.h"
#include "linux/spinlock.h"
#include "linux/utsname.h"
#include "linux/sysrq.h"
#include "linux/seq_file.h"
#include "linux/delay.h"
#include "linux/module.h"
#include "asm/page.h"
#include "asm/pgtable.h"
#include "asm/ptrace.h"
#include "asm/elf.h"
#include "asm/user.h"
#include "asm/setup.h"
#include "ubd_user.h"
#include "asm/current.h"
#include "user_util.h"
#include "kern_util.h"
#include "kern.h"
#include "mem_user.h"
#include "mem.h"
#include "initrd.h"
#include "init.h"
#include "os.h"
#include "choose-mode.h"
#include "mode_kern.h"
#include "mode.h"
#ifdef UML_CONFIG_MODE_SKAS
#include "skas.h"
#endif
#define DEFAULT_COMMAND_LINE "root=98:0"
/* Changed in linux_main and setup_arch, which run before SMP is started */
static char __initdata command_line[COMMAND_LINE_SIZE] = { 0 };
static void __init add_arg(char *arg)
{
if (strlen(command_line) + strlen(arg) + 1 > COMMAND_LINE_SIZE) {
printf("add_arg: Too many command line arguments!\n");
exit(1);
}
if(strlen(command_line) > 0)
strcat(command_line, " ");
strcat(command_line, arg);
}
struct cpuinfo_um boot_cpu_data = {
.loops_per_jiffy = 0,
.ipi_pipe = { -1, -1 }
};
unsigned long thread_saved_pc(struct task_struct *task)
{
return(os_process_pc(CHOOSE_MODE_PROC(thread_pid_tt, thread_pid_skas,
task)));
}
static int show_cpuinfo(struct seq_file *m, void *v)
{
int index = 0;
#ifdef CONFIG_SMP
index = (struct cpuinfo_um *) v - cpu_data;
if (!cpu_online(index))
return 0;
#endif
seq_printf(m, "processor\t: %d\n", index);
seq_printf(m, "vendor_id\t: User Mode Linux\n");
seq_printf(m, "model name\t: UML\n");
seq_printf(m, "mode\t\t: %s\n", CHOOSE_MODE("tt", "skas"));
seq_printf(m, "host\t\t: %s\n", host_info);
seq_printf(m, "bogomips\t: %lu.%02lu\n\n",
loops_per_jiffy/(500000/HZ),
(loops_per_jiffy/(5000/HZ)) % 100);
return(0);
}
static void *c_start(struct seq_file *m, loff_t *pos)
{
return *pos < NR_CPUS ? cpu_data + *pos : NULL;
}
static void *c_next(struct seq_file *m, void *v, loff_t *pos)
{
++*pos;
return c_start(m, pos);
}
static void c_stop(struct seq_file *m, void *v)
{
}
const struct seq_operations cpuinfo_op = {
.start = c_start,
.next = c_next,
.stop = c_stop,
.show = show_cpuinfo,
};
/* Set in linux_main */
unsigned long host_task_size;
unsigned long task_size;
unsigned long uml_start;
/* Set in early boot */
unsigned long uml_physmem;
unsigned long uml_reserved;
unsigned long start_vm;
unsigned long end_vm;
int ncpus = 1;
#ifdef CONFIG_CMDLINE_ON_HOST
/* Pointer set in linux_main, the array itself is private to each thread,
* and changed at address space creation time so this poses no concurrency
* problems.
*/
static char *argv1_begin = NULL;
static char *argv1_end = NULL;
#endif
/* Set in early boot */
static int have_root __initdata = 0;
long long physmem_size = 32 * 1024 * 1024;
void set_cmdline(char *cmd)
{
#ifdef CONFIG_CMDLINE_ON_HOST
char *umid, *ptr;
if(CHOOSE_MODE(honeypot, 0)) return;
umid = get_umid();
if(*umid != '\0'){
snprintf(argv1_begin,
(argv1_end - argv1_begin) * sizeof(*ptr),
"(%s) ", umid);
ptr = &argv1_begin[strlen(argv1_begin)];
}
else ptr = argv1_begin;
snprintf(ptr, (argv1_end - ptr) * sizeof(*ptr), "[%s]", cmd);
memset(argv1_begin + strlen(argv1_begin), '\0',
argv1_end - argv1_begin - strlen(argv1_begin));
#endif
}
static char *usage_string =
"User Mode Linux v%s\n"
" available at http://user-mode-linux.sourceforge.net/\n\n";
static int __init uml_version_setup(char *line, int *add)
{
printf("%s\n", init_utsname()->release);
exit(0);
return 0;
}
__uml_setup("--version", uml_version_setup,
"--version\n"
" Prints the version number of the kernel.\n\n"
);
static int __init uml_root_setup(char *line, int *add)
{
have_root = 1;
return 0;
}
__uml_setup("root=", uml_root_setup,
"root=<file containing the root fs>\n"
" This is actually used by the generic kernel in exactly the same\n"
" way as in any other kernel. If you configure a number of block\n"
" devices and want to boot off something other than ubd0, you \n"
" would use something like:\n"
" root=/dev/ubd5\n\n"
);
#ifndef CONFIG_MODE_TT
static int __init no_skas_debug_setup(char *line, int *add)
{
printf("'debug' is not necessary to gdb UML in skas mode - run \n");
printf("'gdb linux' and disable CONFIG_CMDLINE_ON_HOST if gdb \n");
printf("doesn't work as expected\n");
return 0;
}
__uml_setup("debug", no_skas_debug_setup,
"debug\n"
" this flag is not needed to run gdb on UML in skas mode\n\n"
);
#endif
#ifdef CONFIG_SMP
static int __init uml_ncpus_setup(char *line, int *add)
{
if (!sscanf(line, "%d", &ncpus)) {
printf("Couldn't parse [%s]\n", line);
return -1;
}
return 0;
}
__uml_setup("ncpus=", uml_ncpus_setup,
"ncpus=<# of desired CPUs>\n"
" This tells an SMP kernel how many virtual processors to start.\n\n"
);
#endif
static int force_tt = 0;
#if defined(CONFIG_MODE_TT) && defined(CONFIG_MODE_SKAS)
#define DEFAULT_TT 0
static int __init mode_tt_setup(char *line, int *add)
{
force_tt = 1;
return(0);
}
#else
#ifdef CONFIG_MODE_SKAS
#define DEFAULT_TT 0
static int __init mode_tt_setup(char *line, int *add)
{
printf("CONFIG_MODE_TT disabled - 'mode=tt' ignored\n");
return(0);
}
#else
#ifdef CONFIG_MODE_TT
#define DEFAULT_TT 1
static int __init mode_tt_setup(char *line, int *add)
{
printf("CONFIG_MODE_SKAS disabled - 'mode=tt' redundant\n");
return(0);
}
#endif
#endif
#endif
__uml_setup("mode=tt", mode_tt_setup,
"mode=tt\n"
" When both CONFIG_MODE_TT and CONFIG_MODE_SKAS are enabled, this option\n"
" forces UML to run in tt (tracing thread) mode. It is not the default\n"
" because it's slower and less secure than skas mode.\n\n"
);
int mode_tt = DEFAULT_TT;
static int __init Usage(char *line, int *add)
{
const char **p;
printf(usage_string, init_utsname()->release);
p = &__uml_help_start;
while (p < &__uml_help_end) {
printf("%s", *p);
p++;
}
exit(0);
return 0;
}
__uml_setup("--help", Usage,
"--help\n"
" Prints this message.\n\n"
);
static int __init uml_checksetup(char *line, int *add)
{
struct uml_param *p;
p = &__uml_setup_start;
while(p < &__uml_setup_end) {
int n;
n = strlen(p->str);
if(!strncmp(line, p->str, n)){
if (p->setup_func(line + n, add)) return 1;
}
p++;
}
return 0;
}
static void __init uml_postsetup(void)
{
initcall_t *p;
p = &__uml_postsetup_start;
while(p < &__uml_postsetup_end){
(*p)();
p++;
}
return;
}
/* Set during early boot */
unsigned long brk_start;
unsigned long end_iomem;
EXPORT_SYMBOL(end_iomem);
#define MIN_VMALLOC (32 * 1024 * 1024)
extern char __binary_start;
int __init linux_main(int argc, char **argv)
{
unsigned long avail, diff;
unsigned long virtmem_size, max_physmem;
unsigned int i, add;
char * mode;
for (i = 1; i < argc; i++){
if((i == 1) && (argv[i][0] == ' ')) continue;
add = 1;
uml_checksetup(argv[i], &add);
if (add)
add_arg(argv[i]);
}
if(have_root == 0)
add_arg(DEFAULT_COMMAND_LINE);
os_early_checks();
if (force_tt)
clear_can_do_skas();
mode_tt = force_tt ? 1 : !can_do_skas();
#ifndef CONFIG_MODE_TT
if (mode_tt) {
/*Since CONFIG_MODE_TT is #undef'ed, force_tt cannot be 1. So,
* can_do_skas() returned 0, and the message is correct. */
printf("Support for TT mode is disabled, and no SKAS support is present on the host.\n");
exit(1);
}
#endif
#ifndef CONFIG_MODE_SKAS
mode = "TT";
#else
/* Show to the user the result of selection */
if (mode_tt)
mode = "TT";
else if (proc_mm && ptrace_faultinfo)
mode = "SKAS3";
else
mode = "SKAS0";
#endif
printf("UML running in %s mode\n", mode);
uml_start = (unsigned long) &__binary_start;
host_task_size = CHOOSE_MODE_PROC(set_task_sizes_tt,
set_task_sizes_skas, &task_size);
/*
* Setting up handlers to 'sig_info' struct
*/
os_fill_handlinfo(handlinfo_kern);
brk_start = (unsigned long) sbrk(0);
CHOOSE_MODE_PROC(before_mem_tt, before_mem_skas, brk_start);
/* Increase physical memory size for exec-shield users
so they actually get what they asked for. This should
add zero for non-exec shield users */
diff = UML_ROUND_UP(brk_start) - UML_ROUND_UP(&_end);
if(diff > 1024 * 1024){
printf("Adding %ld bytes to physical memory to account for "
"exec-shield gap\n", diff);
physmem_size += UML_ROUND_UP(brk_start) - UML_ROUND_UP(&_end);
}
uml_physmem = uml_start & PAGE_MASK;
/* Reserve up to 4M after the current brk */
uml_reserved = ROUND_4M(brk_start) + (1 << 22);
setup_machinename(init_utsname()->machine);
#ifdef CONFIG_CMDLINE_ON_HOST
argv1_begin = argv[1];
argv1_end = &argv[1][strlen(argv[1])];
#endif
highmem = 0;
iomem_size = (iomem_size + PAGE_SIZE - 1) & PAGE_MASK;
max_physmem = get_kmem_end() - uml_physmem - iomem_size - MIN_VMALLOC;
/* Zones have to begin on a 1 << MAX_ORDER page boundary,
* so this makes sure that's true for highmem
*/
max_physmem &= ~((1 << (PAGE_SHIFT + MAX_ORDER)) - 1);
if(physmem_size + iomem_size > max_physmem){
highmem = physmem_size + iomem_size - max_physmem;
physmem_size -= highmem;
#ifndef CONFIG_HIGHMEM
highmem = 0;
printf("CONFIG_HIGHMEM not enabled - physical memory shrunk "
"to %Lu bytes\n", physmem_size);
#endif
}
high_physmem = uml_physmem + physmem_size;
end_iomem = high_physmem + iomem_size;
high_memory = (void *) end_iomem;
start_vm = VMALLOC_START;
setup_physmem(uml_physmem, uml_reserved, physmem_size, highmem);
if(init_maps(physmem_size, iomem_size, highmem)){
printf("Failed to allocate mem_map for %Lu bytes of physical "
"memory and %Lu bytes of highmem\n", physmem_size,
highmem);
exit(1);
}
virtmem_size = physmem_size;
avail = get_kmem_end() - start_vm;
if(physmem_size > avail) virtmem_size = avail;
end_vm = start_vm + virtmem_size;
if(virtmem_size < physmem_size)
printf("Kernel virtual memory size shrunk to %lu bytes\n",
virtmem_size);
uml_postsetup();
task_protections((unsigned long) &init_thread_info);
os_flush_stdout();
return(CHOOSE_MODE(start_uml_tt(), start_uml_skas()));
}
extern int uml_exitcode;
static int panic_exit(struct notifier_block *self, unsigned long unused1,
void *unused2)
{
bust_spinlocks(1);
show_regs(&(current->thread.regs));
bust_spinlocks(0);
uml_exitcode = 1;
machine_halt();
return(0);
}
static struct notifier_block panic_exit_notifier = {
.notifier_call = panic_exit,
.next = NULL,
.priority = 0
};
void __init setup_arch(char **cmdline_p)
{
atomic_notifier_chain_register(&panic_notifier_list,
&panic_exit_notifier);
paging_init();
strlcpy(boot_command_line, command_line, COMMAND_LINE_SIZE);
*cmdline_p = command_line;
setup_hostinfo();
}
void __init check_bugs(void)
{
arch_check_bugs();
os_check_bugs();
}
void apply_alternatives(struct alt_instr *start, struct alt_instr *end)
{
}
#ifdef CONFIG_SMP
void alternatives_smp_module_add(struct module *mod, char *name,
void *locks, void *locks_end,
void *text, void *text_end)
{
}
void alternatives_smp_module_del(struct module *mod)
{
}
#endif

39
arch/um/kernel/umid.c Normal file
View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#include "asm/errno.h"
#include "init.h"
#include "os.h"
#include "kern.h"
#include "linux/kernel.h"
/* Changed by set_umid_arg */
static int umid_inited = 0;
static int __init set_umid_arg(char *name, int *add)
{
int err;
if(umid_inited){
printf("umid already set\n");
return 0;
}
*add = 0;
err = set_umid(name);
if(err == -EEXIST)
printf("umid '%s' already in use\n", name);
else if(!err)
umid_inited = 1;
return 0;
}
__uml_setup("umid=", set_umid_arg,
"umid=<name>\n"
" This is used to assign a unique identity to this UML machine and\n"
" is used for naming the pid file and management console socket.\n\n"
);

106
arch/um/kernel/uml.lds.S Normal file
View File

@@ -0,0 +1,106 @@
#include <asm-generic/vmlinux.lds.h>
OUTPUT_FORMAT(ELF_FORMAT)
OUTPUT_ARCH(ELF_ARCH)
ENTRY(_start)
jiffies = jiffies_64;
SECTIONS
{
/* This must contain the right address - not quite the default ELF one.*/
PROVIDE (__executable_start = START);
/* Static binaries stick stuff here, like the sigreturn trampoline,
* invisibly to objdump. So, just make __binary_start equal to the very
* beginning of the executable, and if there are unmapped pages after this,
* they are forever unusable.
*/
__binary_start = START;
. = START + SIZEOF_HEADERS;
#ifdef MODE_TT
.remap_data : { UNMAP_PATH (.data .bss) }
.remap : { UNMAP_PATH (.text) }
. = ALIGN(4096); /* Init code and data */
#endif
_text = .;
_stext = .;
__init_begin = .;
.init.text : {
_sinittext = .;
*(.init.text)
_einittext = .;
}
. = ALIGN(4096);
.text :
{
*(.text)
SCHED_TEXT
LOCK_TEXT
*(.fixup)
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
*(.gnu.linkonce.t*)
. = ALIGN(4096);
__syscall_stub_start = .;
*(.__syscall_stub*)
__syscall_stub_end = .;
. = ALIGN(4096);
}
#include "asm/common.lds.S"
init.data : { *(init.data) }
.data :
{
. = ALIGN(KERNEL_STACK_SIZE); /* init_task */
*(.data.init_task)
*(.data)
*(.gnu.linkonce.d*)
CONSTRUCTORS
}
.data1 : { *(.data1) }
.ctors :
{
*(.ctors)
}
.dtors :
{
*(.dtors)
}
.got : { *(.got.plt) *(.got) }
.dynamic : { *(.dynamic) }
.tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
/* We want the small data sections together, so single-instruction offsets
can access them all, and initialized data all before uninitialized, so
we can shorten the on-disk segment size. */
.sdata : { *(.sdata) }
_edata = .;
PROVIDE (edata = .);
. = ALIGN(0x1000);
.sbss :
{
__bss_start = .;
PROVIDE(_bss_start = .);
*(.sbss)
*(.scommon)
}
.bss :
{
*(.dynbss)
*(.bss)
*(COMMON)
}
_end = .;
PROVIDE (end = .);
STABS_DEBUG
DWARF_DEBUG
}

View File

@@ -0,0 +1,5 @@
#ifdef CONFIG_LD_SCRIPT_STATIC
#include "uml.lds.S"
#else
#include "dyn.lds.S"
#endif