return 1;
}
-static nokprobe_inline long address_ok(struct pt_regs *regs, unsigned long ea, int nb)
+static nokprobe_inline long address_ok(struct pt_regs *regs,
+ unsigned long ea, int nb)
{
if (!user_mode(regs))
return 1;
- return __access_ok(ea, nb, USER_DS);
+ if (__access_ok(ea, nb, USER_DS))
+ return 1;
+ if (__access_ok(ea, 1, USER_DS))
+ /* Access overlaps the end of the user region */
+ regs->dar = USER_DS.seg;
+ else
+ regs->dar = ea;
+ return 0;
}
/*
#endif
static nokprobe_inline int read_mem_aligned(unsigned long *dest,
- unsigned long ea, int nb)
+ unsigned long ea, int nb,
+ struct pt_regs *regs)
{
int err = 0;
unsigned long x = 0;
}
if (!err)
*dest = x;
+ else
+ regs->dar = ea;
return err;
}
* Copy from userspace to a buffer, using the largest possible
* aligned accesses, up to sizeof(long).
*/
-static int nokprobe_inline copy_mem_in(u8 *dest, unsigned long ea, int nb)
+static int nokprobe_inline copy_mem_in(u8 *dest, unsigned long ea, int nb,
+ struct pt_regs *regs)
{
int err = 0;
int c;
break;
#endif
}
- if (err)
+ if (err) {
+ regs->dar = ea;
return err;
+ }
dest += c;
ea += c;
}
u.ul = 0;
i = IS_BE ? sizeof(unsigned long) - nb : 0;
- err = copy_mem_in(&u.b[i], ea, nb);
+ err = copy_mem_in(&u.b[i], ea, nb, regs);
if (!err)
*dest = u.ul;
return err;
if (!address_ok(regs, ea, nb))
return -EFAULT;
if ((ea & (nb - 1)) == 0)
- return read_mem_aligned(dest, ea, nb);
+ return read_mem_aligned(dest, ea, nb, regs);
return read_mem_unaligned(dest, ea, nb, regs);
}
NOKPROBE_SYMBOL(read_mem);
static nokprobe_inline int write_mem_aligned(unsigned long val,
- unsigned long ea, int nb)
+ unsigned long ea, int nb,
+ struct pt_regs *regs)
{
int err = 0;
break;
#endif
}
+ if (err)
+ regs->dar = ea;
return err;
}
* Copy from a buffer to userspace, using the largest possible
* aligned accesses, up to sizeof(long).
*/
-static int nokprobe_inline copy_mem_out(u8 *dest, unsigned long ea, int nb)
+static int nokprobe_inline copy_mem_out(u8 *dest, unsigned long ea, int nb,
+ struct pt_regs *regs)
{
int err = 0;
int c;
break;
#endif
}
- if (err)
+ if (err) {
+ regs->dar = ea;
return err;
+ }
dest += c;
ea += c;
}
u.ul = val;
i = IS_BE ? sizeof(unsigned long) - nb : 0;
- return copy_mem_out(&u.b[i], ea, nb);
+ return copy_mem_out(&u.b[i], ea, nb, regs);
}
/*
if (!address_ok(regs, ea, nb))
return -EFAULT;
if ((ea & (nb - 1)) == 0)
- return write_mem_aligned(val, ea, nb);
+ return write_mem_aligned(val, ea, nb, regs);
return write_mem_unaligned(val, ea, nb, regs);
}
NOKPROBE_SYMBOL(write_mem);
if (!address_ok(regs, ea, nb))
return -EFAULT;
- err = copy_mem_in(u.b, ea, nb);
+ err = copy_mem_in(u.b, ea, nb, regs);
if (err)
return err;
preempt_disable();
u.l[1] = current->thread.TS_FPR(rn);
}
preempt_enable();
- return copy_mem_out(u.b, ea, nb);
+ return copy_mem_out(u.b, ea, nb, regs);
}
NOKPROBE_SYMBOL(do_fp_store);
#endif
return -EFAULT;
/* align to multiple of size */
ea &= ~(size - 1);
- err = copy_mem_in(&u.b[ea & 0xf], ea, size);
+ err = copy_mem_in(&u.b[ea & 0xf], ea, size, regs);
if (err)
return err;
else
u.v = current->thread.vr_state.vr[rn];
preempt_enable();
- return copy_mem_out(&u.b[ea & 0xf], ea, size);
+ return copy_mem_out(&u.b[ea & 0xf], ea, size, regs);
}
#endif /* CONFIG_ALTIVEC */
union vsx_reg buf;
int size = GETSIZE(op->type);
- if (!address_ok(regs, ea, size) || copy_mem_in(mem, ea, size))
+ if (!address_ok(regs, ea, size) || copy_mem_in(mem, ea, size, regs))
return -EFAULT;
emulate_vsx_load(op, &buf, mem);
}
preempt_enable();
emulate_vsx_store(op, &buf, mem);
- return copy_mem_out(mem, ea, size);
+ return copy_mem_out(mem, ea, size, regs);
}
#endif /* CONFIG_VSX */
return -EFAULT;
for (i = 0; i < size; i += sizeof(long)) {
err = __put_user(0, (unsigned long __user *) (ea + i));
- if (err)
+ if (err) {
+ regs->dar = ea;
return err;
+ }
}
return 0;
}
err = emulate_dcbz(ea, regs);
break;
}
- if (err)
+ if (err) {
+ regs->dar = ea;
return 0;
+ }
goto instr_done;
case LARX:
break;
case 16:
err = do_lqarx(ea, ®s->gpr[op.reg]);
- goto ldst_done;
+ break;
#endif
default:
return 0;
}
- if (!err)
+ if (err) {
+ regs->dar = ea;
+ return 0;
+ }
+ if (size < 16)
regs->gpr[op.reg] = val;
goto ldst_done;
regs->ccr = (regs->ccr & 0x0fffffff) |
(cr & 0xe0000000) |
((regs->xer >> 3) & 0x10000000);
+ else
+ regs->dar = ea;
goto ldst_done;
case LOAD: