Commit 8db7b3c5 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'parisc-4.4-4' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/parisc-linux

Pull parisc system call restart fix from Helge Deller:
 "The architectural design of parisc always uses two instructions to
  call kernel syscalls (delayed branch feature).  This means that the
  instruction following the branch (located in the delay slot of the
  branch instruction) is executed before control passes to the branch
  destination.

  Depending on which assembler instruction and how it is used in
  usersapce in the delay slot, this sometimes made restarted syscalls
  like futex() and poll() failing with -ENOSYS"

* 'parisc-4.4-4' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/parisc-linux:
  parisc: Fix syscall restarts
parents 682cb0cd 71a71fb5
......@@ -435,6 +435,55 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs, int in_syscall)
regs->gr[28]);
}
/*
* Check how the syscall number gets loaded into %r20 within
* the delay branch in userspace and adjust as needed.
*/
static void check_syscallno_in_delay_branch(struct pt_regs *regs)
{
u32 opcode, source_reg;
u32 __user *uaddr;
int err;
/* Usually we don't have to restore %r20 (the system call number)
* because it gets loaded in the delay slot of the branch external
* instruction via the ldi instruction.
* In some cases a register-to-register copy instruction might have
* been used instead, in which case we need to copy the syscall
* number into the source register before returning to userspace.
*/
/* A syscall is just a branch, so all we have to do is fiddle the
* return pointer so that the ble instruction gets executed again.
*/
regs->gr[31] -= 8; /* delayed branching */
/* Get assembler opcode of code in delay branch */
uaddr = (unsigned int *) ((regs->gr[31] & ~3) + 4);
err = get_user(opcode, uaddr);
if (err)
return;
/* Check if delay branch uses "ldi int,%r20" */
if ((opcode & 0xffff0000) == 0x34140000)
return; /* everything ok, just return */
/* Check if delay branch uses "nop" */
if (opcode == INSN_NOP)
return;
/* Check if delay branch uses "copy %rX,%r20" */
if ((opcode & 0xffe0ffff) == 0x08000254) {
source_reg = (opcode >> 16) & 31;
regs->gr[source_reg] = regs->gr[20];
return;
}
pr_warn("syscall restart: %s (pid %d): unexpected opcode 0x%08x\n",
current->comm, task_pid_nr(current), opcode);
}
static inline void
syscall_restart(struct pt_regs *regs, struct k_sigaction *ka)
{
......@@ -457,10 +506,7 @@ syscall_restart(struct pt_regs *regs, struct k_sigaction *ka)
}
/* fallthrough */
case -ERESTARTNOINTR:
/* A syscall is just a branch, so all
* we have to do is fiddle the return pointer.
*/
regs->gr[31] -= 8; /* delayed branching */
check_syscallno_in_delay_branch(regs);
break;
}
}
......@@ -510,15 +556,9 @@ insert_restart_trampoline(struct pt_regs *regs)
}
case -ERESTARTNOHAND:
case -ERESTARTSYS:
case -ERESTARTNOINTR: {
/* Hooray for delayed branching. We don't
* have to restore %r20 (the system call
* number) because it gets loaded in the delay
* slot of the branch external instruction.
*/
regs->gr[31] -= 8;
case -ERESTARTNOINTR:
check_syscallno_in_delay_branch(regs);
return;
}
default:
break;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment