| /* |
| * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) |
| * Licensed under the GPL |
| */ |
| |
| #include <stdio.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <linux/stddef.h> |
| #include "ptrace_user.h" |
| /* Grr, asm/user.h includes asm/ptrace.h, so has to follow ptrace_user.h */ |
| #include <asm/user.h> |
| #include "kern_util.h" |
| #include "sysdep/thread.h" |
| #include "user.h" |
| #include "os.h" |
| #include "uml-config.h" |
| #include "user_util.h" |
| |
| int ptrace_getregs(long pid, unsigned long *regs_out) |
| { |
| if (ptrace(PTRACE_GETREGS, pid, 0, regs_out) < 0) |
| return -errno; |
| return 0; |
| } |
| |
| int ptrace_setregs(long pid, unsigned long *regs) |
| { |
| if (ptrace(PTRACE_SETREGS, pid, 0, regs) < 0) |
| return -errno; |
| return 0; |
| } |
| |
| int ptrace_getfpregs(long pid, unsigned long *regs) |
| { |
| if (ptrace(PTRACE_GETFPREGS, pid, 0, regs) < 0) |
| return -errno; |
| return 0; |
| } |
| |
| int ptrace_setfpregs(long pid, unsigned long *regs) |
| { |
| if (ptrace(PTRACE_SETFPREGS, pid, 0, regs) < 0) |
| return -errno; |
| return 0; |
| } |
| |
| /* All the below stuff is of interest for TT mode only */ |
| static void write_debugregs(int pid, unsigned long *regs) |
| { |
| struct user *dummy; |
| int nregs, i; |
| |
| dummy = NULL; |
| nregs = ARRAY_SIZE(dummy->u_debugreg); |
| for(i = 0; i < nregs; i++){ |
| if((i == 4) || (i == 5)) continue; |
| if(ptrace(PTRACE_POKEUSR, pid, &dummy->u_debugreg[i], |
| regs[i]) < 0) |
| printk("write_debugregs - ptrace failed on " |
| "register %d, value = 0x%lx, errno = %d\n", i, |
| regs[i], errno); |
| } |
| } |
| |
| static void read_debugregs(int pid, unsigned long *regs) |
| { |
| struct user *dummy; |
| int nregs, i; |
| |
| dummy = NULL; |
| nregs = ARRAY_SIZE(dummy->u_debugreg); |
| for(i = 0; i < nregs; i++){ |
| regs[i] = ptrace(PTRACE_PEEKUSR, pid, |
| &dummy->u_debugreg[i], 0); |
| } |
| } |
| |
| /* Accessed only by the tracing thread */ |
| static unsigned long kernel_debugregs[8] = { [ 0 ... 7 ] = 0 }; |
| |
| void arch_enter_kernel(void *task, int pid) |
| { |
| read_debugregs(pid, TASK_DEBUGREGS(task)); |
| write_debugregs(pid, kernel_debugregs); |
| } |
| |
| void arch_leave_kernel(void *task, int pid) |
| { |
| read_debugregs(pid, kernel_debugregs); |
| write_debugregs(pid, TASK_DEBUGREGS(task)); |
| } |
| |
| #ifdef UML_CONFIG_PT_PROXY |
| /* Accessed only by the tracing thread */ |
| static int debugregs_seq; |
| |
| /* Only called by the ptrace proxy */ |
| void ptrace_pokeuser(unsigned long addr, unsigned long data) |
| { |
| if((addr < offsetof(struct user, u_debugreg[0])) || |
| (addr > offsetof(struct user, u_debugreg[7]))) |
| return; |
| addr -= offsetof(struct user, u_debugreg[0]); |
| addr = addr >> 2; |
| if(kernel_debugregs[addr] == data) return; |
| |
| kernel_debugregs[addr] = data; |
| debugregs_seq++; |
| } |
| |
| static void update_debugregs_cb(void *arg) |
| { |
| int pid = *((int *) arg); |
| |
| write_debugregs(pid, kernel_debugregs); |
| } |
| |
| /* Optimized out in its header when not defined */ |
| void update_debugregs(int seq) |
| { |
| int me; |
| |
| if(seq == debugregs_seq) return; |
| |
| me = os_getpid(); |
| initial_thread_cb(update_debugregs_cb, &me); |
| } |
| #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: |
| */ |