diff options
Diffstat (limited to 'arch/um/os-Linux/process.c')
-rw-r--r-- | arch/um/os-Linux/process.c | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/arch/um/os-Linux/process.c b/arch/um/os-Linux/process.c new file mode 100644 index 000000000..b3e0d4093 --- /dev/null +++ b/arch/um/os-Linux/process.c @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2015 Thomas Meyer (thomas@m3y3r.de) + * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) + * Licensed under the GPL + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/ptrace.h> +#include <sys/wait.h> +#include <asm/unistd.h> +#include <init.h> +#include <longjmp.h> +#include <os.h> + +#define ARBITRARY_ADDR -1 +#define FAILURE_PID -1 + +#define STAT_PATH_LEN sizeof("/proc/#######/stat\0") +#define COMM_SCANF "%*[^)])" + +unsigned long os_process_pc(int pid) +{ + char proc_stat[STAT_PATH_LEN], buf[256]; + unsigned long pc = ARBITRARY_ADDR; + int fd, err; + + sprintf(proc_stat, "/proc/%d/stat", pid); + fd = open(proc_stat, O_RDONLY, 0); + if (fd < 0) { + printk(UM_KERN_ERR "os_process_pc - couldn't open '%s', " + "errno = %d\n", proc_stat, errno); + goto out; + } + CATCH_EINTR(err = read(fd, buf, sizeof(buf))); + if (err < 0) { + printk(UM_KERN_ERR "os_process_pc - couldn't read '%s', " + "err = %d\n", proc_stat, errno); + goto out_close; + } + os_close_file(fd); + pc = ARBITRARY_ADDR; + if (sscanf(buf, "%*d " COMM_SCANF " %*c %*d %*d %*d %*d %*d %*d %*d " + "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d " + "%*d %*d %*d %*d %*d %lu", &pc) != 1) + printk(UM_KERN_ERR "os_process_pc - couldn't find pc in '%s'\n", + buf); + out_close: + close(fd); + out: + return pc; +} + +int os_process_parent(int pid) +{ + char stat[STAT_PATH_LEN]; + char data[256]; + int parent = FAILURE_PID, n, fd; + + if (pid == -1) + return parent; + + snprintf(stat, sizeof(stat), "/proc/%d/stat", pid); + fd = open(stat, O_RDONLY, 0); + if (fd < 0) { + printk(UM_KERN_ERR "Couldn't open '%s', errno = %d\n", stat, + errno); + return parent; + } + + CATCH_EINTR(n = read(fd, data, sizeof(data))); + close(fd); + + if (n < 0) { + printk(UM_KERN_ERR "Couldn't read '%s', errno = %d\n", stat, + errno); + return parent; + } + + parent = FAILURE_PID; + n = sscanf(data, "%*d " COMM_SCANF " %*c %d", &parent); + if (n != 1) + printk(UM_KERN_ERR "Failed to scan '%s'\n", data); + + return parent; +} + +void os_alarm_process(int pid) +{ + kill(pid, SIGALRM); +} + +void os_stop_process(int pid) +{ + kill(pid, SIGSTOP); +} + +void os_kill_process(int pid, int reap_child) +{ + kill(pid, SIGKILL); + if (reap_child) + CATCH_EINTR(waitpid(pid, NULL, __WALL)); +} + +/* Kill off a ptraced child by all means available. kill it normally first, + * then PTRACE_KILL it, then PTRACE_CONT it in case it's in a run state from + * which it can't exit directly. + */ + +void os_kill_ptraced_process(int pid, int reap_child) +{ + kill(pid, SIGKILL); + ptrace(PTRACE_KILL, pid); + ptrace(PTRACE_CONT, pid); + if (reap_child) + CATCH_EINTR(waitpid(pid, NULL, __WALL)); +} + +/* Don't use the glibc version, which caches the result in TLS. It misses some + * syscalls, and also breaks with clone(), which does not unshare the TLS. + */ + +int os_getpid(void) +{ + return syscall(__NR_getpid); +} + +int os_getpgrp(void) +{ + return getpgrp(); +} + +int os_map_memory(void *virt, int fd, unsigned long long off, unsigned long len, + int r, int w, int x) +{ + void *loc; + int prot; + + prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) | + (x ? PROT_EXEC : 0); + + loc = mmap64((void *) virt, len, prot, MAP_SHARED | MAP_FIXED, + fd, off); + if (loc == MAP_FAILED) + return -errno; + return 0; +} + +int os_protect_memory(void *addr, unsigned long len, int r, int w, int x) +{ + int prot = ((r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) | + (x ? PROT_EXEC : 0)); + + if (mprotect(addr, len, prot) < 0) + return -errno; + + return 0; +} + +int os_unmap_memory(void *addr, int len) +{ + int err; + + err = munmap(addr, len); + if (err < 0) + return -errno; + return 0; +} + +#ifndef MADV_REMOVE +#define MADV_REMOVE KERNEL_MADV_REMOVE +#endif + +int os_drop_memory(void *addr, int length) +{ + int err; + + err = madvise(addr, length, MADV_REMOVE); + if (err < 0) + err = -errno; + return err; +} + +int __init can_drop_memory(void) +{ + void *addr; + int fd, ok = 0; + + printk(UM_KERN_INFO "Checking host MADV_REMOVE support..."); + fd = create_mem_file(UM_KERN_PAGE_SIZE); + if (fd < 0) { + printk(UM_KERN_ERR "Creating test memory file failed, " + "err = %d\n", -fd); + goto out; + } + + addr = mmap64(NULL, UM_KERN_PAGE_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) { + printk(UM_KERN_ERR "Mapping test memory file failed, " + "err = %d\n", -errno); + goto out_close; + } + + if (madvise(addr, UM_KERN_PAGE_SIZE, MADV_REMOVE) != 0) { + printk(UM_KERN_ERR "MADV_REMOVE failed, err = %d\n", -errno); + goto out_unmap; + } + + printk(UM_KERN_CONT "OK\n"); + ok = 1; + +out_unmap: + munmap(addr, UM_KERN_PAGE_SIZE); +out_close: + close(fd); +out: + return ok; +} + +static int os_page_mincore(void *addr) +{ + char vec[2]; + int ret; + + ret = mincore(addr, UM_KERN_PAGE_SIZE, vec); + if (ret < 0) { + if (errno == ENOMEM || errno == EINVAL) + return 0; + else + return -errno; + } + + return vec[0] & 1; +} + +int os_mincore(void *addr, unsigned long len) +{ + char *vec; + int ret, i; + + if (len <= UM_KERN_PAGE_SIZE) + return os_page_mincore(addr); + + vec = calloc(1, (len + UM_KERN_PAGE_SIZE - 1) / UM_KERN_PAGE_SIZE); + if (!vec) + return -ENOMEM; + + ret = mincore(addr, UM_KERN_PAGE_SIZE, vec); + if (ret < 0) { + if (errno == ENOMEM || errno == EINVAL) + ret = 0; + else + ret = -errno; + + goto out; + } + + for (i = 0; i < ((len + UM_KERN_PAGE_SIZE - 1) / UM_KERN_PAGE_SIZE); i++) { + if (!(vec[i] & 1)) { + ret = 0; + goto out; + } + } + + ret = 1; +out: + free(vec); + return ret; +} + +void init_new_thread_signals(void) +{ + set_handler(SIGSEGV); + set_handler(SIGTRAP); + set_handler(SIGFPE); + set_handler(SIGILL); + set_handler(SIGBUS); + signal(SIGHUP, SIG_IGN); + set_handler(SIGIO); + signal(SIGWINCH, SIG_IGN); +} |