1 /* 2 * Copyright (C) 2003 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 3 * Copyright 2003 PathScale, Inc. 4 * 5 * Licensed under the GPL 6 */ 7 8 #include <linux/sched.h> 9 #include <linux/sched/mm.h> 10 #include <linux/syscalls.h> 11 #include <linux/uaccess.h> 12 #include <asm/prctl.h> /* XXX This should get the constants from libc */ 13 #include <registers.h> 14 #include <os.h> 15 16 long arch_prctl(struct task_struct *task, int option, 17 unsigned long __user *arg2) 18 { 19 unsigned long *ptr = arg2, tmp; 20 long ret; 21 int pid = task->mm->context.id.u.pid; 22 23 /* 24 * With ARCH_SET_FS (and ARCH_SET_GS is treated similarly to 25 * be safe), we need to call arch_prctl on the host because 26 * setting %fs may result in something else happening (like a 27 * GDT or thread.fs being set instead). So, we let the host 28 * fiddle the registers and thread struct and restore the 29 * registers afterwards. 30 * 31 * So, the saved registers are stored to the process (this 32 * needed because a stub may have been the last thing to run), 33 * arch_prctl is run on the host, then the registers are read 34 * back. 35 */ 36 switch (option) { 37 case ARCH_SET_FS: 38 case ARCH_SET_GS: 39 ret = restore_pid_registers(pid, ¤t->thread.regs.regs); 40 if (ret) 41 return ret; 42 break; 43 case ARCH_GET_FS: 44 case ARCH_GET_GS: 45 /* 46 * With these two, we read to a local pointer and 47 * put_user it to the userspace pointer that we were 48 * given. If addr isn't valid (because it hasn't been 49 * faulted in or is just bogus), we want put_user to 50 * fault it in (or return -EFAULT) instead of having 51 * the host return -EFAULT. 52 */ 53 ptr = &tmp; 54 } 55 56 ret = os_arch_prctl(pid, option, ptr); 57 if (ret) 58 return ret; 59 60 switch (option) { 61 case ARCH_SET_FS: 62 current->thread.arch.fs = (unsigned long) ptr; 63 ret = save_registers(pid, ¤t->thread.regs.regs); 64 break; 65 case ARCH_SET_GS: 66 ret = save_registers(pid, ¤t->thread.regs.regs); 67 break; 68 case ARCH_GET_FS: 69 ret = put_user(tmp, arg2); 70 break; 71 case ARCH_GET_GS: 72 ret = put_user(tmp, arg2); 73 break; 74 } 75 76 return ret; 77 } 78 79 SYSCALL_DEFINE2(arch_prctl, int, option, unsigned long, arg2) 80 { 81 return arch_prctl(current, option, (unsigned long __user *) arg2); 82 } 83 84 void arch_switch_to(struct task_struct *to) 85 { 86 if ((to->thread.arch.fs == 0) || (to->mm == NULL)) 87 return; 88 89 arch_prctl(to, ARCH_SET_FS, (void __user *) to->thread.arch.fs); 90 } 91 92 SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len, 93 unsigned long, prot, unsigned long, flags, 94 unsigned long, fd, unsigned long, off) 95 { 96 if (off & ~PAGE_MASK) 97 return -EINVAL; 98 99 return ksys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT); 100 } 101