blob: 58f51667e2e4beb82ec794cf618bf7026cfcb66c [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Jeff Dikeba180fd2007-10-16 01:27:00 -07002 * Copyright (C) 2003 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 * Copyright 2003 PathScale, Inc.
4 *
5 * Licensed under the GPL
6 */
7
Al Virod2ce4e92012-09-20 09:28:25 -04008#include <linux/sched.h>
Ingo Molnar589ee622017-02-04 00:16:44 +01009#include <linux/sched/mm.h>
Kyle Hueyff3f0972017-03-20 01:16:21 -070010#include <linux/syscalls.h>
Richard Weinberger7017f422015-05-12 00:13:05 +020011#include <linux/uaccess.h>
Al Virod2ce4e92012-09-20 09:28:25 -040012#include <asm/prctl.h> /* XXX This should get the constants from libc */
13#include <os.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070014
Kyle Hueyd5827992017-03-20 16:05:35 -070015long arch_prctl(struct task_struct *task, int option,
Kyle Huey17a6e1b2017-03-20 01:16:22 -070016 unsigned long __user *arg2)
Linus Torvalds1da177e2005-04-16 15:20:36 -070017{
Kyle Huey17a6e1b2017-03-20 01:16:22 -070018 unsigned long *ptr = arg2, tmp;
Jeff Dikef3555592007-02-10 01:44:29 -080019 long ret;
Jeff Dike6c738ff2007-10-16 01:27:06 -070020 int pid = task->mm->context.id.u.pid;
Linus Torvalds1da177e2005-04-16 15:20:36 -070021
Jeff Dikef3555592007-02-10 01:44:29 -080022 /*
23 * With ARCH_SET_FS (and ARCH_SET_GS is treated similarly to
24 * be safe), we need to call arch_prctl on the host because
25 * setting %fs may result in something else happening (like a
Jeff Dike6e6d74c2007-02-10 01:44:30 -080026 * GDT or thread.fs being set instead). So, we let the host
27 * fiddle the registers and thread struct and restore the
28 * registers afterwards.
Jeff Dikef3555592007-02-10 01:44:29 -080029 *
30 * So, the saved registers are stored to the process (this
31 * needed because a stub may have been the last thing to run),
32 * arch_prctl is run on the host, then the registers are read
33 * back.
34 */
Kyle Hueydd939382017-03-20 01:16:20 -070035 switch (option) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070036 case ARCH_SET_FS:
Paolo 'Blaisorblade' Giarrussof767b022005-05-28 15:52:03 -070037 case ARCH_SET_GS:
Jeff Dike3e6f2ac2008-02-04 22:30:58 -080038 ret = restore_registers(pid, &current->thread.regs.regs);
39 if (ret)
40 return ret;
Jeff Dikeba180fd2007-10-16 01:27:00 -070041 break;
42 case ARCH_GET_FS:
43 case ARCH_GET_GS:
44 /*
45 * With these two, we read to a local pointer and
46 * put_user it to the userspace pointer that we were
47 * given. If addr isn't valid (because it hasn't been
48 * faulted in or is just bogus), we want put_user to
49 * fault it in (or return -EFAULT) instead of having
50 * the host return -EFAULT.
51 */
52 ptr = &tmp;
53 }
Jeff Dikef3555592007-02-10 01:44:29 -080054
Kyle Hueydd939382017-03-20 01:16:20 -070055 ret = os_arch_prctl(pid, option, ptr);
Jeff Dikeba180fd2007-10-16 01:27:00 -070056 if (ret)
57 return ret;
Jeff Dikef3555592007-02-10 01:44:29 -080058
Kyle Hueydd939382017-03-20 01:16:20 -070059 switch (option) {
Jeff Dikef3555592007-02-10 01:44:29 -080060 case ARCH_SET_FS:
Jeff Dike44f5c4c2007-03-07 20:41:26 -080061 current->thread.arch.fs = (unsigned long) ptr;
Jeff Dike3e6f2ac2008-02-04 22:30:58 -080062 ret = save_registers(pid, &current->thread.regs.regs);
Jeff Dike44f5c4c2007-03-07 20:41:26 -080063 break;
Jeff Dikef3555592007-02-10 01:44:29 -080064 case ARCH_SET_GS:
Jeff Dike3e6f2ac2008-02-04 22:30:58 -080065 ret = save_registers(pid, &current->thread.regs.regs);
Paolo 'Blaisorblade' Giarrussof767b022005-05-28 15:52:03 -070066 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 case ARCH_GET_FS:
Kyle Huey17a6e1b2017-03-20 01:16:22 -070068 ret = put_user(tmp, arg2);
Jeff Dikeba180fd2007-10-16 01:27:00 -070069 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -070070 case ARCH_GET_GS:
Kyle Huey17a6e1b2017-03-20 01:16:22 -070071 ret = put_user(tmp, arg2);
Jeff Dikeba180fd2007-10-16 01:27:00 -070072 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 }
74
Jeff Dikef3555592007-02-10 01:44:29 -080075 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -070076}
Linus Torvalds1da177e2005-04-16 15:20:36 -070077
Kyle Huey17a6e1b2017-03-20 01:16:22 -070078SYSCALL_DEFINE2(arch_prctl, int, option, unsigned long, arg2)
Linus Torvalds1da177e2005-04-16 15:20:36 -070079{
Kyle Huey17a6e1b2017-03-20 01:16:22 -070080 return arch_prctl(current, option, (unsigned long __user *) arg2);
Linus Torvalds1da177e2005-04-16 15:20:36 -070081}
82
Karol Swietlicki291248f2008-02-04 22:30:49 -080083void arch_switch_to(struct task_struct *to)
Jeff Dikef3555592007-02-10 01:44:29 -080084{
Jeff Dikeba180fd2007-10-16 01:27:00 -070085 if ((to->thread.arch.fs == 0) || (to->mm == NULL))
86 return;
Jeff Dikef3555592007-02-10 01:44:29 -080087
Jeff Dikeba180fd2007-10-16 01:27:00 -070088 arch_prctl(to, ARCH_SET_FS, (void __user *) to->thread.arch.fs);
Jeff Dikef3555592007-02-10 01:44:29 -080089}