This is the new_mm, switch_mm, and /proc/<pid>/mm implementation for 32- and 64-bit x86 and UML, plus 32-bit support on 64-bit x86.
diff --git a/arch/um/include/skas_ptrace.h b/arch/um/include/skas_ptrace.h index cd2327d..38ec9fd 100644 --- a/arch/um/include/skas_ptrace.h +++ b/arch/um/include/skas_ptrace.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000, 2001, 2002 Jeff Dike ([EMAIL PROTECTED]) + * Copyright (C) 2000 - 2008 Jeff Dike ([EMAIL PROTECTED],linux.intel}.com) * Licensed under the GPL */ @@ -7,19 +7,10 @@ #define __SKAS_PTRACE_H #define PTRACE_FAULTINFO 52 -#define PTRACE_SWITCH_MM 55 +#ifndef OLD_PTRACE_SWITCH_MM +#define OLD_PTRACE_SWITCH_MM 55 +#endif #include "sysdep/skas_ptrace.h" #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: - */ diff --git a/arch/um/include/sysdep-i386/ptrace_user.h b/arch/um/include/sysdep-i386/ptrace_user.h index 7565072..9a4892d 100644 --- a/arch/um/include/sysdep-i386/ptrace_user.h +++ b/arch/um/include/sysdep-i386/ptrace_user.h @@ -43,6 +43,8 @@ #define FP_SIZE ((HOST_XFP_SIZE > HOST_FP_SIZE) ? HOST_XFP_SIZE : HOST_FP_SIZE) +#define FP_SIZE ((HOST_XFP_SIZE > HOST_FP_SIZE) ? HOST_XFP_SIZE : HOST_FP_SIZE) + #ifndef FRAME_SIZE #define FRAME_SIZE (17) #endif diff --git a/arch/um/include/sysdep-x86_64/ptrace_user.h b/arch/um/include/sysdep-x86_64/ptrace_user.h index 45c0bd8..4e10c60 100644 --- a/arch/um/include/sysdep-x86_64/ptrace_user.h +++ b/arch/um/include/sysdep-x86_64/ptrace_user.h @@ -72,6 +72,8 @@ #define FP_SIZE (HOST_FP_SIZE) +#define FP_SIZE (HOST_FP_SIZE) + #endif /* diff --git a/arch/um/kernel/ptrace.c b/arch/um/kernel/ptrace.c index 47b57b4..6b6855a 100644 --- a/arch/um/kernel/ptrace.c +++ b/arch/um/kernel/ptrace.c @@ -192,7 +192,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) } #endif #ifdef CONFIG_PROC_MM - case PTRACE_SWITCH_MM: { + case OLD_PTRACE_SWITCH_MM: { struct mm_struct *old = child->mm; struct mm_struct *new = proc_mm_get_mm(data); @@ -292,3 +292,36 @@ void syscall_trace(struct uml_pt_regs *regs, int entryexit) current->exit_code = 0; } } + +int ptrace_to_pt_regs(struct pt_regs *to, struct user_regs __user *from) +{ + struct user_regs regs; + int rem; + + rem = copy_from_user(®s, from, sizeof(regs)); + if (rem) + return -EFAULT; + + memcpy(&to->regs.gp, ®s.regs, sizeof(to->regs.gp)); + + return put_fp_registers(userspace_pid[0], + (unsigned long *) ®s.fpregs); +} + +int pt_regs_to_ptrace(struct user_regs __user *to, struct pt_regs *from) +{ + struct user_regs regs; + int err; + + err = get_fp_registers(userspace_pid[0], + (unsigned long *) ®s.fpregs); + if (err) + return err; + + memcpy(®s.regs, &from->regs.gp, sizeof(regs.regs)); + + if(copy_to_user(to, ®s, sizeof(regs))) + return -EFAULT; + + return 0; +} diff --git a/arch/um/kernel/syscall.c b/arch/um/kernel/syscall.c index 9cffc62..a9c2f6f 100644 --- a/arch/um/kernel/syscall.c +++ b/arch/um/kernel/syscall.c @@ -148,3 +148,13 @@ int kernel_execve(const char *filename, char *const argv[], char *const envp[]) return ret; } + +extern long do_switch_mm(int fd, long __user *save, long __user *new, + unsigned long ip, unsigned long sp, + struct pt_regs *regs); + +long sys_switch_mm(int fd, long __user *save, long __user *new, + unsigned long ip, unsigned long sp) +{ + return do_switch_mm(fd, save, new, ip, sp, ¤t->thread.regs); +} diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c index 1e8cba6..cbb7986 100644 --- a/arch/um/os-Linux/skas/process.c +++ b/arch/um/os-Linux/skas/process.c @@ -716,7 +716,7 @@ void __switch_mm(struct mm_id *mm_idp) /* FIXME: need cpu pid in __switch_mm */ if (proc_mm) { - err = ptrace(PTRACE_SWITCH_MM, userspace_pid[0], 0, + err = ptrace(OLD_PTRACE_SWITCH_MM, userspace_pid[0], 0, mm_idp->u.mm_fd); if (err) { printk(UM_KERN_ERR "__switch_mm - PTRACE_SWITCH_MM " diff --git a/arch/um/sys-x86_64/syscall_table.c b/arch/um/sys-x86_64/syscall_table.c index c128eb8..8b5c216 100644 --- a/arch/um/sys-x86_64/syscall_table.c +++ b/arch/um/sys-x86_64/syscall_table.c @@ -39,6 +39,7 @@ #define stub_rt_sigsuspend sys_rt_sigsuspend #define stub_sigaltstack sys_sigaltstack #define stub_rt_sigreturn sys_rt_sigreturn +#define stub_switch_mm sys_switch_mm #define __SYSCALL(nr, sym) extern asmlinkage void sym(void) ; #undef _ASM_X86_64_UNISTD_H_ diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index 8022d3c..8273782 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -373,6 +373,7 @@ quiet_ni_syscall: PTREGSCALL stub32_vfork, sys_vfork, %rdi PTREGSCALL stub32_iopl, sys_iopl, %rsi PTREGSCALL stub32_rt_sigsuspend, sys_rt_sigsuspend, %rdx + PTREGSCALL stub32_switch_mm, sys_switch_mm, %r9 ENTRY(ia32_ptregs_common) popq %r11 @@ -727,4 +728,6 @@ ia32_sys_call_table: .quad sys32_fallocate .quad compat_sys_timerfd_settime /* 325 */ .quad compat_sys_timerfd_gettime + .quad sys_new_mm + .quad stub32_switch_mm ia32_syscall_end: diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index c20c9e7..bb573ef 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -425,6 +425,7 @@ END(\label) PTREGSCALL stub_rt_sigsuspend, sys_rt_sigsuspend, %rdx PTREGSCALL stub_sigaltstack, sys_sigaltstack, %rdx PTREGSCALL stub_iopl, sys_iopl, %rsi + PTREGSCALL stub_switch_mm, sys_switch_mm, %r9 ENTRY(ptregscall_common) popq %r11 diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index eb92ccb..de84950 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c @@ -307,8 +307,7 @@ static int set_flags(struct task_struct *task, unsigned long value) return 0; } -static int putreg(struct task_struct *child, - unsigned long offset, unsigned long value) +int putreg(struct task_struct *child, unsigned long offset, unsigned long value) { switch (offset) { case offsetof(struct user_regs_struct, cs): @@ -360,7 +359,7 @@ static int putreg(struct task_struct *child, return 0; } -static unsigned long getreg(struct task_struct *task, unsigned long offset) +unsigned long getreg(struct task_struct *task, unsigned long offset) { switch (offset) { case offsetof(struct user_regs_struct, cs): @@ -1036,7 +1035,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) value); \ break -static int putreg32(struct task_struct *child, unsigned regno, u32 value) +int putreg32(struct task_struct *child, unsigned regno, u32 value) { struct pt_regs *regs = task_pt_regs(child); @@ -1101,7 +1100,7 @@ static int putreg32(struct task_struct *child, unsigned regno, u32 value) offsetof(struct user_regs_struct, rs)); \ break -static int getreg32(struct task_struct *child, unsigned regno, u32 *val) +int getreg32(struct task_struct *child, unsigned regno, u32 *val) { struct pt_regs *regs = task_pt_regs(child); @@ -1254,6 +1253,7 @@ asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data) case PTRACE_SETOPTIONS: case PTRACE_SET_THREAD_AREA: case PTRACE_GET_THREAD_AREA: + case PTRACE_SWITCH_MM: #ifdef X86_BTS case PTRACE_BTS_CONFIG: case PTRACE_BTS_STATUS: @@ -1533,6 +1533,64 @@ out: return 1; } +int ptrace_to_pt_regs(struct pt_regs *regs, struct __user user_regs *ptrace) +{ + struct user_fxsr_struct *fp; + int i, err; + + if (!access_ok(VERIFY_READ, ptrace, sizeof(*ptrace))) + return -EFAULT; + + for (i = 0; i < FRAME_SIZE; i++) { + unsigned long n; + + if (__get_user(n, &ptrace->regs[i])) + return -EFAULT; + err = putreg(current, i * 4, n); + if (err) + return err; + } + + if (__get_user(fp, &ptrace->fp_state)) + return -EFAULT; + + if (fp == NULL) { + clear_used_math(); + return 0; + } + + set_used_math(); + + return xfpregs_set(current, NULL, 0, sizeof(*fp), NULL, fp); +} + +int pt_regs_to_ptrace(struct __user user_regs *ptrace, struct pt_regs *regs) +{ + int i; + + if (!access_ok(VERIFY_WRITE, ptrace, sizeof(*ptrace))) + return -EFAULT; + + for (i = 0; i < FRAME_SIZE; i++) { + unsigned long n = getreg(current, i * 4); + if (__put_user(n, &ptrace->regs[i])) + return -EFAULT; + } + + if (!used_math()) { + if (__put_user(NULL, &ptrace->fp_state)) + return -EFAULT; + return 0; + } + + if (__put_user(&ptrace->fpregs, &ptrace->fp_state)) + return -EFAULT; + + clear_used_math(); + + return xfpregs_get(current, NULL, 0, sizeof(ptrace->fpregs), NULL, + &ptrace->fpregs); +} #else /* CONFIG_X86_64 */ static void syscall_trace(struct pt_regs *regs) @@ -1593,4 +1651,115 @@ asmlinkage void syscall_trace_leave(struct pt_regs *regs) syscall_trace(regs); } +int ptrace_to_pt_regs(struct pt_regs *regs, struct user_regs *ptrace) +{ + struct user_i387_struct *fp; + int i, err; + +#ifdef CONFIG_IA32_EMULATION + if (test_thread_flag(TIF_IA32)) { + for (i = 0; i < MAX_REG32_NR; i++) { + err = putreg32(current, i * 4, ptrace->u.regs32[i]); + if (err) + return err; + } + + return 0; + } +#endif + for (i = 0; i < MAX_REG_NR; i++){ + if(i * 8 == offsetof(struct user_regs_struct, fs)) + continue; + + err = putreg(current, i * 8, ptrace->u.regs64.regs[i]); + if (err) + return err; + } + + if (__get_user(fp, &ptrace->u.regs64.fp_state)) + return -EFAULT; + + if (fp == NULL) { + clear_used_math(); + return 0; + } + + set_used_math(); + + return xfpregs_set(current, NULL, 0, sizeof(*fp), NULL, fp); +} + +extern int getreg32(struct task_struct *child, unsigned regno, u32 *val); + +int pt_regs_to_ptrace(struct __user user_regs *ptrace, struct pt_regs *regs) +{ + int i, err; + +#ifdef CONFIG_IA32_EMULATION + if (test_thread_flag(TIF_IA32)) { + if (!access_ok(VERIFY_WRITE, &ptrace->u.regs32, + sizeof(&ptrace->u.regs32))) + return -EFAULT; + + for (i = 0; i < ARRAY_SIZE(ptrace->u.regs32); i++) { + u32 n; + + err = getreg32(current, i * 4, &n); + if (err) + return err; + + err = __put_user(n, &ptrace->u.regs32[i]); + if (err) + return err; + } + + return 0; + } +#endif + if (!access_ok(VERIFY_WRITE, &ptrace->u.regs64, + sizeof(ptrace->u.regs64))) + return -EFAULT; + + for (i = 0; i < ARRAY_SIZE(ptrace->u.regs64.regs); i++) { + unsigned long n = getreg(current, i * 8); + err = __put_user(n, &ptrace->u.regs64.regs[i]); + if (err) + return err; + } + + if (!used_math()) { + if (__put_user(NULL, &ptrace->u.regs64.fp_state)) + return -EFAULT; + return 0; + } + + if (__put_user(&ptrace->u.regs64.fpregs, &ptrace->u.regs64.fp_state)) + return -EFAULT; + + clear_used_math(); + + return xfpregs_get(current, NULL, 0, sizeof(ptrace->u.regs64.fpregs), + NULL, &ptrace->u.regs64.fpregs); +} + +#define RIP_INDEX (128 / sizeof(long)) +#define RSP_INDEX (152 / sizeof(long)) + +unsigned long ptrace_ip(struct user_regs *regs) +{ +#ifdef CONFIG_IA32_EMULATION + if (test_thread_flag(TIF_IA32)) + return ptrace_ip32(regs->u.regs32); +#endif + return regs->u.regs64.regs[RIP_INDEX]; +} + +unsigned long ptrace_sp(struct user_regs *regs) +{ +#ifdef CONFIG_IA32_EMULATION + if (test_thread_flag(TIF_IA32)) + return ptrace_sp32(regs->u.regs32); +#endif + return regs->u.regs64.regs[RSP_INDEX]; +} #endif /* CONFIG_X86_32 */ diff --git a/arch/x86/kernel/sys_i386_32.c b/arch/x86/kernel/sys_i386_32.c index a86d26f..23f6aff 100644 --- a/arch/x86/kernel/sys_i386_32.c +++ b/arch/x86/kernel/sys_i386_32.c @@ -21,6 +21,7 @@ #include <asm/uaccess.h> #include <asm/unistd.h> +#include <asm/user.h> /* * sys_pipe() is the normal C calling standard for creating @@ -261,3 +262,14 @@ int kernel_execve(const char *filename, char *const argv[], char *const envp[]) : "0" (__NR_execve),"ri" (filename),"c" (argv), "d" (envp) : "memory"); return __res; } + +extern long do_switch_mm(int fd, struct __user user_regs *save, + struct __user user_regs *new, unsigned long ip, + unsigned long sp, struct pt_regs *regs); + +asmlinkage long sys_switch_mm(struct pt_regs regs) +{ + return do_switch_mm(regs.bx, (struct __user user_regs *) regs.cx, + (struct __user user_regs *) regs.dx, regs.si, + regs.di, ®s); +} diff --git a/arch/x86/kernel/sys_x86_64.c b/arch/x86/kernel/sys_x86_64.c index bd802a5..b3c98f5 100644 --- a/arch/x86/kernel/sys_x86_64.c +++ b/arch/x86/kernel/sys_x86_64.c @@ -251,3 +251,14 @@ asmlinkage long sys_uname(struct new_utsname __user * name) err |= copy_to_user(&name->machine, "i686", 5); return err ? -EFAULT : 0; } + +extern long do_switch_mm(int fd, struct __user user_regs *save, + struct __user user_regs *new, unsigned long ip, + unsigned long sp, struct pt_regs *regs); + +asmlinkage long sys_switch_mm(int fd, struct __user user_regs *save, + struct __user user_regs *new, unsigned long ip, + unsigned long sp, struct pt_regs *regs) +{ + return do_switch_mm(fd, save, new, ip, sp, regs); +} diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S index adff556..27f20f0 100644 --- a/arch/x86/kernel/syscall_table_32.S +++ b/arch/x86/kernel/syscall_table_32.S @@ -326,3 +326,5 @@ ENTRY(sys_call_table) .long sys_fallocate .long sys_timerfd_settime /* 325 */ .long sys_timerfd_gettime + .long sys_new_mm + .long sys_switch_mm diff --git a/fs/proc/base.c b/fs/proc/base.c index 81d7d14..082f349 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2279,6 +2279,37 @@ static int proc_pid_io_accounting(struct task_struct *task, char *buffer) } #endif +static int proc_pid_mm_open(struct inode *inode, struct file *file) +{ + struct task_struct *task = pid_task(proc_pid(inode), PIDTYPE_PID); + struct mm_struct *mm; + + if (task == NULL) + return -ENOENT; + + mm = get_task_mm(task); + if (mm == NULL) + return -EINVAL; + + file->private_data = mm; + return 0; +} + +static int proc_pid_mm_release(struct inode *inode, struct file *file) +{ + struct mm_struct *mm = file->private_data; + + if(mm != NULL) + mmput(mm); + + return 0; +} + +const struct file_operations proc_pid_mm_operations = { + .open = proc_pid_mm_open, + .release = proc_pid_mm_release, +}; + /* * Thread groups */ @@ -2350,6 +2381,7 @@ static const struct pid_entry tgid_base_stuff[] = { #ifdef CONFIG_TASK_IO_ACCOUNTING INF("io", S_IRUGO, pid_io_accounting), #endif + REG("mm", S_IRUSR | S_IWUSR, pid_mm), }; static int proc_tgid_base_readdir(struct file * filp, diff --git a/include/asm-um/ptrace-generic.h b/include/asm-um/ptrace-generic.h index 6aefcd3..46f8a3f 100644 --- a/include/asm-um/ptrace-generic.h +++ b/include/asm-um/ptrace-generic.h @@ -34,6 +34,15 @@ struct pt_regs { #define instruction_pointer(regs) PT_REGS_IP(regs) +struct user_regs { + unsigned long regs[MAX_REG_NR]; + void *ptr; + unsigned long fpregs[FP_SIZE]; +}; + +extern int ptrace_to_pt_regs(struct pt_regs *to, struct user_regs __user *from); +extern int pt_regs_to_ptrace(struct user_regs __user *to, struct pt_regs *from); + struct task_struct; extern long subarch_ptrace(struct task_struct *child, long request, long addr, diff --git a/include/asm-um/ptrace-i386.h b/include/asm-um/ptrace-i386.h index b2d24c5..8c9c160 100644 --- a/include/asm-um/ptrace-i386.h +++ b/include/asm-um/ptrace-i386.h @@ -8,8 +8,11 @@ #define HOST_AUDIT_ARCH AUDIT_ARCH_I386 -#include "linux/compiler.h" -#include "asm/ptrace-generic.h" +#include "user_constants.h" +#define FP_SIZE ((HOST_XFP_SIZE > HOST_FP_SIZE) ? HOST_XFP_SIZE : HOST_FP_SIZE) + +#include <linux/compiler.h> +#include <asm/ptrace-generic.h> #include <asm/user.h> #include "sysdep/ptrace.h" @@ -40,6 +43,12 @@ #define user_mode(r) UPT_IS_USER(&(r)->regs) +#define pt_regs_ip(r) (r).regs.gp[EIP] +#define pt_regs_sp(r) (r).regs.gp[UESP] + +#define ptrace_ip(r) (r)->regs[EIP] +#define ptrace_sp(r) (r)->regs[UESP] + /* * Forward declaration to avoid including sysdep/tls.h, which causes a * circular include, and compilation failures. diff --git a/include/asm-um/ptrace-x86_64.h b/include/asm-um/ptrace-x86_64.h index 4c47535..21345b5 100644 --- a/include/asm-um/ptrace-x86_64.h +++ b/include/asm-um/ptrace-x86_64.h @@ -7,6 +7,9 @@ #ifndef __UM_PTRACE_X86_64_H #define __UM_PTRACE_X86_64_H +#include "user_constants.h" +#define FP_SIZE (HOST_FP_SIZE) + #include "linux/compiler.h" #include "asm/errno.h" #include "asm/host_ldt.h" @@ -62,6 +65,12 @@ #define PT_FIX_EXEC_STACK(sp) do ; while(0) +#define pt_regs_ip(r) (r).regs.gp[RIP / sizeof(long)] +#define pt_regs_sp(r) (r).regs.gp[RSP / sizeof(long)] + +#define ptrace_ip(r) (r)->regs[RIP / sizeof(long)] +#define ptrace_sp(r) (r)->regs[RSP / sizeof(long)] + #define profile_pc(regs) PT_REGS_IP(regs) static inline int ptrace_get_thread_area(struct task_struct *child, int idx, diff --git a/include/asm-x86/Kbuild b/include/asm-x86/Kbuild index 3b8160a..45f5d02 100644 --- a/include/asm-x86/Kbuild +++ b/include/asm-x86/Kbuild @@ -21,5 +21,6 @@ unifdef-y += posix_types_64.h unifdef-y += ptrace.h unifdef-y += unistd_32.h unifdef-y += unistd_64.h +unifdef-y += user.h unifdef-y += vm86.h unifdef-y += vsyscall.h diff --git a/include/asm-x86/ptrace.h b/include/asm-x86/ptrace.h index d9e04b4..046fb58 100644 --- a/include/asm-x86/ptrace.h +++ b/include/asm-x86/ptrace.h @@ -3,7 +3,7 @@ #include <linux/compiler.h> /* For __user */ #include <asm/ptrace-abi.h> - +#include <asm/user.h> #ifndef __ASSEMBLY__ @@ -55,6 +55,24 @@ struct pt_regs { int ss; }; +#define pt_regs_ip(r) (r).ip +#define pt_regs_sp(r) (r).sp + +struct user_regs { + unsigned long regs[FRAME_SIZE]; + struct user_fxsr_struct *fp_state; + struct user_fxsr_struct fpregs; +}; + +#define ptrace_ip(r) (r)->regs.ip +#define ptrace_sp(r) (r)->regs.sp + +struct pt_regs; +extern int ptrace_to_pt_regs(struct pt_regs *regs, + struct user_regs __user *ptrace); +extern int pt_regs_to_ptrace(struct __user user_regs *ptrace, + struct pt_regs *regs); + #include <asm/vm86.h> #include <asm/segment.h> @@ -227,6 +245,46 @@ extern int do_get_thread_area(struct task_struct *p, int idx, extern int do_set_thread_area(struct task_struct *p, int idx, struct user_desc __user *info, int can_allocate); +#ifdef CONFIG_X86_64 +#ifdef CONFIG_IA32_EMULATION +#define MAX_REG32_NR 17 + +#define EIP 12 +#define UESP 15 + +#define ptrace_ip32(regs) (unsigned long) (regs)[EIP] +#define ptrace_sp32(regs) (unsigned long) (regs)[UESP] + +#endif + +#define MAX_REG_NR (sizeof(struct user_regs_struct) / sizeof(long)) + +struct user_regs { + union { + struct { + unsigned long regs[MAX_REG_NR]; + struct user_i387_struct *fp_state; + struct user_i387_struct fpregs; + } regs64; +#ifdef CONFIG_IA32_EMULATION + u32 regs32[MAX_REG32_NR]; +#endif + } u; +}; + +#define pt_regs_ip(regs) (regs).ip +#define pt_regs_sp(regs) (regs).sp + +extern unsigned long ptrace_ip(struct user_regs *regs); +extern unsigned long ptrace_sp(struct user_regs *regs); + +extern int ptrace_to_pt_regs(struct pt_regs *regs, + struct user_regs __user *ptrace); +extern int pt_regs_to_ptrace(struct __user user_regs *ptrace, + struct pt_regs *regs); +#else +#endif + #endif /* __KERNEL__ */ #endif /* !__ASSEMBLY__ */ diff --git a/include/asm-x86/unistd_32.h b/include/asm-x86/unistd_32.h index 984123a..5f8f291 100644 --- a/include/asm-x86/unistd_32.h +++ b/include/asm-x86/unistd_32.h @@ -332,6 +332,8 @@ #define __NR_fallocate 324 #define __NR_timerfd_settime 325 #define __NR_timerfd_gettime 326 +#define __NR_new_mm 327 +#define __NR_switch_mm 328 #ifdef __KERNEL__ diff --git a/include/asm-x86/unistd_64.h b/include/asm-x86/unistd_64.h index 3883ceb..a674098 100644 --- a/include/asm-x86/unistd_64.h +++ b/include/asm-x86/unistd_64.h @@ -639,6 +639,10 @@ __SYSCALL(__NR_fallocate, sys_fallocate) __SYSCALL(__NR_timerfd_settime, sys_timerfd_settime) #define __NR_timerfd_gettime 287 __SYSCALL(__NR_timerfd_gettime, sys_timerfd_gettime) +#define __NR_new_mm 288 +__SYSCALL(__NR_new_mm, sys_new_mm) +#define __NR_switch_mm 289 +__SYSCALL(__NR_switch_mm, stub_switch_mm) #ifndef __NO_STUBS diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index ebe0c17..a8ef98a 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -21,6 +21,8 @@ #define PTRACE_SYSCALL 24 +#define PTRACE_SWITCH_MM 34 + /* 0x4200-0x4300 are reserved for architecture-independent additions. */ #define PTRACE_SETOPTIONS 0x4200 #define PTRACE_GETEVENTMSG 0x4201 diff --git a/include/linux/sched.h b/include/linux/sched.h index 6a1e7af..7360fde 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1750,6 +1750,7 @@ static inline int sas_ss_flags(unsigned long sp) * Routines for handling mm_structs */ extern struct mm_struct * mm_alloc(void); +extern struct mm_struct *dup_mm(struct task_struct *tsk); /* mmdrop drops the mm and the page tables */ extern void __mmdrop(struct mm_struct *); diff --git a/kernel/fork.c b/kernel/fork.c index 9c042f9..4ca580a 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -498,7 +498,7 @@ void mm_release(struct task_struct *tsk, struct mm_struct *mm) * Allocate a new mm structure and copy contents from the * mm structure of the passed in task structure. */ -static struct mm_struct *dup_mm(struct task_struct *tsk) +struct mm_struct *dup_mm(struct task_struct *tsk) { struct mm_struct *mm, *oldmm = current->mm; int err; diff --git a/kernel/ptrace.c b/kernel/ptrace.c index fdb34e8..2200f84 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -420,6 +420,8 @@ static int ptrace_resume(struct task_struct *child, long request, long data) return 0; } +extern int do_switch(struct task_struct *task, int fd); + int ptrace_request(struct task_struct *child, long request, long addr, long data) { @@ -471,6 +473,10 @@ int ptrace_request(struct task_struct *child, long request, return 0; return ptrace_resume(child, request, SIGKILL); + case PTRACE_SWITCH_MM: + ret = do_switch(child, data); + break; + default: break; } diff --git a/mm/Makefile b/mm/Makefile index a5b0dd9..123ca7d 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -4,8 +4,8 @@ mmu-y := nommu.o mmu-$(CONFIG_MMU) := fremap.o highmem.o madvise.o memory.o mincore.o \ - mlock.o mmap.o mprotect.o mremap.o msync.o rmap.o \ - vmalloc.o + mlock.o mmap.o mmfs.o mprotect.o mremap.o msync.o \ + rmap.o vmalloc.o obj-y := bootmem.o filemap.o mempool.o oom_kill.o fadvise.o \ page_alloc.o page-writeback.o pdflush.o \ diff --git a/mm/mmfs.c b/mm/mmfs.c new file mode 100644 index 0000000..247f7a3 --- /dev/null +++ b/mm/mmfs.c @@ -0,0 +1,215 @@ +#define __FRAME_OFFSETS +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/mount.h> +#include <linux/sched.h> +#include <asm/mmu_context.h> +#include <asm/ptrace.h> +#include <asm/uaccess.h> +#include <asm/user.h> + +static int release_mm(struct inode *inode, struct file *file) +{ + struct mm_struct *mm = file->private_data; + + mmput(mm); + return 0; +} + +#define MM_MAGIC 0xE0AAC500 + +static int mm_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, + struct vfsmount *mnt) +{ + return get_sb_pseudo(fs_type, "mm:", NULL, MM_MAGIC, mnt); +} + +static struct vfsmount *mm_mnt; + +static struct file_system_type mm_fs_type = { + .name = "mm", + .get_sb = mm_get_sb, + .kill_sb = kill_anon_super, +}; + +static int __init init_mm_fs(void) +{ + int err; + + err = register_filesystem(&mm_fs_type); + if (err) + return err; + + mm_mnt = kern_mount(&mm_fs_type); + if (IS_ERR(mm_mnt)) { + err = PTR_ERR(mm_mnt); + unregister_filesystem(&mm_fs_type); + } + + return err; +} + +static void __exit exit_mm_fs(void) +{ + unregister_filesystem(&mm_fs_type); + mntput(mm_mnt); +} + +fs_initcall(init_mm_fs); +module_exit(exit_mm_fs); + +static int mm_delete_dentry(struct dentry *dentry) +{ + /* + * At creation time, we pretended this dentry was hashed + * (by clearing DCACHE_UNHASHED bit in d_flags) + * At delete time, we restore the truth : not hashed. + * (so that dput() can proceed correctly) + */ + dentry->d_flags |= DCACHE_UNHASHED; + return 0; +} + +/* + * pipefs_dname() is called from d_path(). + */ +static char *mm_dname(struct dentry *dentry, char *buffer, int buflen) +{ + return dynamic_dname(dentry, buffer, buflen, "mm:[%lu]", + dentry->d_inode->i_ino); +} + +static struct dentry_operations mm_dentry_operations = { + .d_delete = mm_delete_dentry, + .d_dname = mm_dname, +}; + +static struct file_operations mm_fops = { + .release = release_mm, +}; + +asmlinkage long sys_new_mm(void) +{ + struct file *file; + struct mm_struct *mm; + struct inode *inode; + struct dentry *dentry; + struct qstr name = { .name = "" }; + int err, fd; + + mm = dup_mm(current); + if (mm == NULL) + return -ENOMEM; + + fd = get_unused_fd(); + if (fd < 0) { + err = fd; + goto out_free; + } + + err = -ENOMEM; + dentry = d_alloc(mm_mnt->mnt_sb->s_root, &name); + if (dentry == NULL) + goto out_put; + + dentry->d_op = &mm_dentry_operations; + dentry->d_flags &= ~DCACHE_UNHASHED; + + inode = new_inode(mm_mnt->mnt_sb); + if (inode == NULL) + goto out_dput; + + inode->i_mode = S_IRUSR; + inode->i_uid = current->fsuid; + inode->i_gid = current->fsgid; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + + d_instantiate(dentry, inode); + + file = alloc_file(mm_mnt, dentry, FMODE_READ, &mm_fops); + if (file == NULL) + goto out_dput; + + file->f_flags = O_RDONLY; + file->private_data = mm; + + fd_install(fd, file); + + return fd; + + out_dput: + dput(dentry); + out_put: + put_unused_fd(fd); + out_free: + mmput(mm); + return err; +} + +void do_switch_mm_struct(struct task_struct *task, struct mm_struct *new) +{ + struct mm_struct *old = task->mm; + + task_lock(task); + + atomic_inc(&new->mm_users); + task->mm = new; + task->active_mm = new; + + if (task == current) + switch_mm(old, task->mm, task); + + task_unlock(task); + + mmput(old); +} + +extern const struct file_operations proc_pid_mm_operations; + +int do_switch(struct task_struct *task, int fd) +{ + struct file *file = fget(fd); + int err; + + if (!file) + return -EBADF; + + err = -EINVAL; + if ((file->f_op != &mm_fops) && (file->f_op != &proc_pid_mm_operations)) + goto out; + + do_switch_mm_struct(task, file->private_data); + + err = 0; + + out: + fput(file); + return err; +} + +long do_switch_mm(int fd, struct __user user_regs *save, + struct __user user_regs *new, unsigned long ip, + unsigned long sp, struct pt_regs *regs) +{ + int ret; + + if (current->mm == NULL) + return -EINVAL; + + if ((save != NULL) && pt_regs_to_ptrace(save, regs)) + return -EFAULT; + + ret = do_switch(current, fd); + if (ret) + return ret; + + if (new != NULL) + ret = ptrace_to_pt_regs(regs, new); + else { + pt_regs_ip(*regs) = ip; + pt_regs_sp(*regs) = sp; + } + + return ret; +} ------------------------------------------------------------------------- This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008. http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/ _______________________________________________ User-mode-linux-user mailing list User-mode-linux-user@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/user-mode-linux-user