These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / arch / powerpc / include / asm / syscall.h
index ff21b7a..ab9f3f0 100644 (file)
 extern const unsigned long sys_call_table[];
 #endif /* CONFIG_FTRACE_SYSCALLS */
 
-static inline long syscall_get_nr(struct task_struct *task,
-                                 struct pt_regs *regs)
+static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs)
 {
-       return TRAP(regs) == 0xc00 ? regs->gpr[0] : -1L;
+       /*
+        * Note that we are returning an int here. That means 0xffffffff, ie.
+        * 32-bit negative 1, will be interpreted as -1 on a 64-bit kernel.
+        * This is important for seccomp so that compat tasks can set r0 = -1
+        * to reject the syscall.
+        */
+       return TRAP(regs) == 0xc00 ? regs->gpr[0] : -1;
 }
 
 static inline void syscall_rollback(struct task_struct *task,
@@ -34,12 +39,6 @@ static inline void syscall_rollback(struct task_struct *task,
        regs->gpr[3] = regs->orig_gpr3;
 }
 
-static inline long syscall_get_error(struct task_struct *task,
-                                    struct pt_regs *regs)
-{
-       return (regs->ccr & 0x10000000) ? -regs->gpr[3] : 0;
-}
-
 static inline long syscall_get_return_value(struct task_struct *task,
                                            struct pt_regs *regs)
 {
@@ -50,9 +49,15 @@ static inline void syscall_set_return_value(struct task_struct *task,
                                            struct pt_regs *regs,
                                            int error, long val)
 {
+       /*
+        * In the general case it's not obvious that we must deal with CCR
+        * here, as the syscall exit path will also do that for us. However
+        * there are some places, eg. the signal code, which check ccr to
+        * decide if the value in r3 is actually an error.
+        */
        if (error) {
                regs->ccr |= 0x10000000L;
-               regs->gpr[3] = -error;
+               regs->gpr[3] = error;
        } else {
                regs->ccr &= ~0x10000000L;
                regs->gpr[3] = val;
@@ -64,19 +69,22 @@ static inline void syscall_get_arguments(struct task_struct *task,
                                         unsigned int i, unsigned int n,
                                         unsigned long *args)
 {
+       unsigned long val, mask = -1UL;
+
        BUG_ON(i + n > 6);
-#ifdef CONFIG_PPC64
-       if (test_tsk_thread_flag(task, TIF_32BIT)) {
-               /*
-                * Zero-extend 32-bit argument values.  The high bits are
-                * garbage ignored by the actual syscall dispatch.
-                */
-               while (n-- > 0)
-                       args[n] = (u32) regs->gpr[3 + i + n];
-               return;
-       }
+
+#ifdef CONFIG_COMPAT
+       if (test_tsk_thread_flag(task, TIF_32BIT))
+               mask = 0xffffffff;
 #endif
-       memcpy(args, &regs->gpr[3 + i], n * sizeof(args[0]));
+       while (n--) {
+               if (n == 0 && i == 0)
+                       val = regs->orig_gpr3;
+               else
+                       val = regs->gpr[3 + i + n];
+
+               args[n] = val & mask;
+       }
 }
 
 static inline void syscall_set_arguments(struct task_struct *task,
@@ -86,6 +94,10 @@ static inline void syscall_set_arguments(struct task_struct *task,
 {
        BUG_ON(i + n > 6);
        memcpy(&regs->gpr[3 + i], args, n * sizeof(args[0]));
+
+       /* Also copy the first argument into orig_gpr3 */
+       if (i == 0 && n > 0)
+               regs->orig_gpr3 = args[0];
 }
 
 static inline int syscall_get_arch(void)