powerpc/64s: system call support for scv/rfscv instructions
[linux-2.6-microblaze.git] / arch / powerpc / kernel / syscall_64.c
index 79edba3..a783fd3 100644 (file)
@@ -60,6 +60,11 @@ notrace long system_call_exception(long r3, long r4, long r5,
        local_irq_enable();
 
        if (unlikely(current_thread_info()->flags & _TIF_SYSCALL_DOTRACE)) {
+               if (unlikely(regs->trap == 0x7ff0)) {
+                       /* Unsupported scv vector */
+                       _exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
+                       return regs->gpr[3];
+               }
                /*
                 * We use the return value of do_syscall_trace_enter() as the
                 * syscall number. If the syscall was rejected for any reason
@@ -78,6 +83,11 @@ notrace long system_call_exception(long r3, long r4, long r5,
                r8 = regs->gpr[8];
 
        } else if (unlikely(r0 >= NR_syscalls)) {
+               if (unlikely(regs->trap == 0x7ff0)) {
+                       /* Unsupported scv vector */
+                       _exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
+                       return regs->gpr[3];
+               }
                return -ENOSYS;
        }
 
@@ -105,16 +115,20 @@ notrace long system_call_exception(long r3, long r4, long r5,
  * local irqs must be disabled. Returns false if the caller must re-enable
  * them, check for new work, and try again.
  */
-static notrace inline bool prep_irq_for_enabled_exit(void)
+static notrace inline bool prep_irq_for_enabled_exit(bool clear_ri)
 {
        /* This must be done with RI=1 because tracing may touch vmaps */
        trace_hardirqs_on();
 
        /* This pattern matches prep_irq_for_idle */
-       __hard_EE_RI_disable();
+       if (clear_ri)
+               __hard_EE_RI_disable();
+       else
+               __hard_irq_disable();
        if (unlikely(lazy_irq_pending_nocheck())) {
                /* Took an interrupt, may have more exit work to do. */
-               __hard_RI_enable();
+               if (clear_ri)
+                       __hard_RI_enable();
                trace_hardirqs_off();
                local_paca->irq_happened |= PACA_IRQ_HARD_DIS;
 
@@ -136,7 +150,8 @@ static notrace inline bool prep_irq_for_enabled_exit(void)
  * because RI=0 and soft mask state is "unreconciled", so it is marked notrace.
  */
 notrace unsigned long syscall_exit_prepare(unsigned long r3,
-                                          struct pt_regs *regs)
+                                          struct pt_regs *regs,
+                                          long scv)
 {
        unsigned long *ti_flagsp = &current_thread_info()->flags;
        unsigned long ti_flags;
@@ -151,7 +166,7 @@ notrace unsigned long syscall_exit_prepare(unsigned long r3,
 
        ti_flags = *ti_flagsp;
 
-       if (unlikely(r3 >= (unsigned long)-MAX_ERRNO)) {
+       if (unlikely(r3 >= (unsigned long)-MAX_ERRNO) && !scv) {
                if (likely(!(ti_flags & (_TIF_NOERROR | _TIF_RESTOREALL)))) {
                        r3 = -r3;
                        regs->ccr |= 0x10000000; /* Set SO bit in CR */
@@ -211,7 +226,8 @@ again:
                }
        }
 
-       if (unlikely(!prep_irq_for_enabled_exit())) {
+       /* scv need not set RI=0 because SRRs are not used */
+       if (unlikely(!prep_irq_for_enabled_exit(!scv))) {
                local_irq_enable();
                goto again;
        }
@@ -282,7 +298,7 @@ again:
                }
        }
 
-       if (unlikely(!prep_irq_for_enabled_exit())) {
+       if (unlikely(!prep_irq_for_enabled_exit(true))) {
                local_irq_enable();
                local_irq_disable();
                goto again;
@@ -345,7 +361,7 @@ again:
                        }
                }
 
-               if (unlikely(!prep_irq_for_enabled_exit())) {
+               if (unlikely(!prep_irq_for_enabled_exit(true))) {
                        /*
                         * Can't local_irq_restore to replay if we were in
                         * interrupt context. Must replay directly.