Merge tag 'arm64-stable' of git://git.kernel.org/pub/scm/linux/kernel/git/cmarinas...
[linux-block.git] / arch / s390 / kernel / ptrace.c
index a314c57f4e94a5a91c162de68360556babd71f8a..e9fadb04e3c61e0b71b6eb238a12941359edba0d 100644 (file)
@@ -47,7 +47,7 @@ enum s390_regset {
        REGSET_GENERAL_EXTENDED,
 };
 
-void update_per_regs(struct task_struct *task)
+void update_cr_regs(struct task_struct *task)
 {
        struct pt_regs *regs = task_pt_regs(task);
        struct thread_struct *thread = &task->thread;
@@ -56,17 +56,25 @@ void update_per_regs(struct task_struct *task)
 #ifdef CONFIG_64BIT
        /* Take care of the enable/disable of transactional execution. */
        if (MACHINE_HAS_TE) {
-               unsigned long cr0, cr0_new;
+               unsigned long cr[3], cr_new[3];
 
-               __ctl_store(cr0, 0, 0);
-               /* set or clear transaction execution bits 8 and 9. */
+               __ctl_store(cr, 0, 2);
+               cr_new[1] = cr[1];
+               /* Set or clear transaction execution TXC/PIFO bits 8 and 9. */
                if (task->thread.per_flags & PER_FLAG_NO_TE)
-                       cr0_new = cr0 & ~(3UL << 54);
+                       cr_new[0] = cr[0] & ~(3UL << 54);
                else
-                       cr0_new = cr0 | (3UL << 54);
-               /* Only load control register 0 if necessary. */
-               if (cr0 != cr0_new)
-                       __ctl_load(cr0_new, 0, 0);
+                       cr_new[0] = cr[0] | (3UL << 54);
+               /* Set or clear transaction execution TDC bits 62 and 63. */
+               cr_new[2] = cr[2] & ~3UL;
+               if (task->thread.per_flags & PER_FLAG_TE_ABORT_RAND) {
+                       if (task->thread.per_flags & PER_FLAG_TE_ABORT_RAND_TEND)
+                               cr_new[2] |= 1UL;
+                       else
+                               cr_new[2] |= 2UL;
+               }
+               if (memcmp(&cr_new, &cr, sizeof(cr)))
+                       __ctl_load(cr_new, 0, 2);
        }
 #endif
        /* Copy user specified PER registers */
@@ -100,14 +108,14 @@ void user_enable_single_step(struct task_struct *task)
 {
        set_tsk_thread_flag(task, TIF_SINGLE_STEP);
        if (task == current)
-               update_per_regs(task);
+               update_cr_regs(task);
 }
 
 void user_disable_single_step(struct task_struct *task)
 {
        clear_tsk_thread_flag(task, TIF_SINGLE_STEP);
        if (task == current)
-               update_per_regs(task);
+               update_cr_regs(task);
 }
 
 /*
@@ -447,6 +455,26 @@ long arch_ptrace(struct task_struct *child, long request,
                if (!MACHINE_HAS_TE)
                        return -EIO;
                child->thread.per_flags |= PER_FLAG_NO_TE;
+               child->thread.per_flags &= ~PER_FLAG_TE_ABORT_RAND;
+               return 0;
+       case PTRACE_TE_ABORT_RAND:
+               if (!MACHINE_HAS_TE || (child->thread.per_flags & PER_FLAG_NO_TE))
+                       return -EIO;
+               switch (data) {
+               case 0UL:
+                       child->thread.per_flags &= ~PER_FLAG_TE_ABORT_RAND;
+                       break;
+               case 1UL:
+                       child->thread.per_flags |= PER_FLAG_TE_ABORT_RAND;
+                       child->thread.per_flags |= PER_FLAG_TE_ABORT_RAND_TEND;
+                       break;
+               case 2UL:
+                       child->thread.per_flags |= PER_FLAG_TE_ABORT_RAND;
+                       child->thread.per_flags &= ~PER_FLAG_TE_ABORT_RAND_TEND;
+                       break;
+               default:
+                       return -EINVAL;
+               }
                return 0;
        default:
                /* Removing high order bit from addr (only for 31 bit). */