#include #include #include #include long do_arch_prctl(struct task_struct *task, int code, unsigned long addr) { int ret = 0; int doit = task == current; switch (code) { case ARCH_SET_GS: if (addr >= task->arch.addr_limit) return -EPERM; task->arch.thread.gsindex = 0; task->arch.thread.gs = addr; if (doit) { /* The kernel's %gs is currently loaded, so this call is needed to set the user version. */ load_gs_index(0); ret = checking_wrmsrl(MSR_KERNEL_GS_BASE, addr); } break; case ARCH_SET_FS: /* Not strictly needed for fs, but do it for symmetry with gs */ if (addr >= task->arch.addr_limit) return -EPERM; task->arch.thread.fsindex = 0; task->arch.thread.fs = addr; if (doit) { /* The kernel doesn't use %fs so we can set it directly. set the selector to 0 to not confuse __switch_to */ asm volatile("movl %0,%%fs" :: "r" (0)); ret = checking_wrmsrl(MSR_FS_BASE, addr); } break; case ARCH_GET_FS: { unsigned long base; if (doit) rdmsrl(MSR_FS_BASE, base); else base = task->arch.thread.fs; ret = put_user(base, (unsigned long __user *)addr); break; } case ARCH_GET_GS: { unsigned long base; unsigned gsindex; if (doit) { asm("movl %%gs,%0" : "=r" (gsindex)); if (gsindex) rdmsrl(MSR_KERNEL_GS_BASE, base); else base = task->arch.thread.gs; } else base = task->arch.thread.gs; ret = put_user(base, (unsigned long __user *)addr); break; } default: ret = -EINVAL; break; } return ret; } long sys_arch_prctl(int code, unsigned long addr) { return do_arch_prctl(current, code, addr); }