--- /dev/null
+#include <lwk/kernel.h>
+#include <lwk/task.h>
+#include <arch/prctl.h>
+#include <arch/uaccess.h>
+
+
+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);
+}
+