Commit b9da9c8a authored by Paul Mackerras's avatar Paul Mackerras Committed by Michael Ellerman

powerpc: Set regs->dar if memory access fails in emulate_step()

This adds code to the instruction emulation code to set regs->dar
to the address of any memory access that fails.  This address is
not necessarily the same as the effective address of the instruction,
because if the memory access is unaligned, it might cross a page
boundary and fault on the second page.
Signed-off-by: default avatarPaul Mackerras <paulus@ozlabs.org>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent b2543f7b
...@@ -103,11 +103,19 @@ static nokprobe_inline int branch_taken(unsigned int instr, ...@@ -103,11 +103,19 @@ static nokprobe_inline int branch_taken(unsigned int instr,
return 1; 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)) if (!user_mode(regs))
return 1; 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;
} }
/* /*
...@@ -210,7 +218,8 @@ static nokprobe_inline unsigned long byterev_8(unsigned long x) ...@@ -210,7 +218,8 @@ static nokprobe_inline unsigned long byterev_8(unsigned long x)
#endif #endif
static nokprobe_inline int read_mem_aligned(unsigned long *dest, 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; int err = 0;
unsigned long x = 0; unsigned long x = 0;
...@@ -233,6 +242,8 @@ static nokprobe_inline int read_mem_aligned(unsigned long *dest, ...@@ -233,6 +242,8 @@ static nokprobe_inline int read_mem_aligned(unsigned long *dest,
} }
if (!err) if (!err)
*dest = x; *dest = x;
else
regs->dar = ea;
return err; return err;
} }
...@@ -240,7 +251,8 @@ static nokprobe_inline int read_mem_aligned(unsigned long *dest, ...@@ -240,7 +251,8 @@ static nokprobe_inline int read_mem_aligned(unsigned long *dest,
* Copy from userspace to a buffer, using the largest possible * Copy from userspace to a buffer, using the largest possible
* aligned accesses, up to sizeof(long). * 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 err = 0;
int c; int c;
...@@ -268,8 +280,10 @@ static int nokprobe_inline copy_mem_in(u8 *dest, unsigned long ea, int nb) ...@@ -268,8 +280,10 @@ static int nokprobe_inline copy_mem_in(u8 *dest, unsigned long ea, int nb)
break; break;
#endif #endif
} }
if (err) if (err) {
regs->dar = ea;
return err; return err;
}
dest += c; dest += c;
ea += c; ea += c;
} }
...@@ -289,7 +303,7 @@ static nokprobe_inline int read_mem_unaligned(unsigned long *dest, ...@@ -289,7 +303,7 @@ static nokprobe_inline int read_mem_unaligned(unsigned long *dest,
u.ul = 0; u.ul = 0;
i = IS_BE ? sizeof(unsigned long) - nb : 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) if (!err)
*dest = u.ul; *dest = u.ul;
return err; return err;
...@@ -306,13 +320,14 @@ static int read_mem(unsigned long *dest, unsigned long ea, int nb, ...@@ -306,13 +320,14 @@ static int read_mem(unsigned long *dest, unsigned long ea, int nb,
if (!address_ok(regs, ea, nb)) if (!address_ok(regs, ea, nb))
return -EFAULT; return -EFAULT;
if ((ea & (nb - 1)) == 0) 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); return read_mem_unaligned(dest, ea, nb, regs);
} }
NOKPROBE_SYMBOL(read_mem); NOKPROBE_SYMBOL(read_mem);
static nokprobe_inline int write_mem_aligned(unsigned long val, 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; int err = 0;
...@@ -332,6 +347,8 @@ static nokprobe_inline int write_mem_aligned(unsigned long val, ...@@ -332,6 +347,8 @@ static nokprobe_inline int write_mem_aligned(unsigned long val,
break; break;
#endif #endif
} }
if (err)
regs->dar = ea;
return err; return err;
} }
...@@ -339,7 +356,8 @@ static nokprobe_inline int write_mem_aligned(unsigned long val, ...@@ -339,7 +356,8 @@ static nokprobe_inline int write_mem_aligned(unsigned long val,
* Copy from a buffer to userspace, using the largest possible * Copy from a buffer to userspace, using the largest possible
* aligned accesses, up to sizeof(long). * 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 err = 0;
int c; int c;
...@@ -367,8 +385,10 @@ static int nokprobe_inline copy_mem_out(u8 *dest, unsigned long ea, int nb) ...@@ -367,8 +385,10 @@ static int nokprobe_inline copy_mem_out(u8 *dest, unsigned long ea, int nb)
break; break;
#endif #endif
} }
if (err) if (err) {
regs->dar = ea;
return err; return err;
}
dest += c; dest += c;
ea += c; ea += c;
} }
...@@ -387,7 +407,7 @@ static nokprobe_inline int write_mem_unaligned(unsigned long val, ...@@ -387,7 +407,7 @@ static nokprobe_inline int write_mem_unaligned(unsigned long val,
u.ul = val; u.ul = val;
i = IS_BE ? sizeof(unsigned long) - nb : 0; 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);
} }
/* /*
...@@ -400,7 +420,7 @@ static int write_mem(unsigned long val, unsigned long ea, int nb, ...@@ -400,7 +420,7 @@ static int write_mem(unsigned long val, unsigned long ea, int nb,
if (!address_ok(regs, ea, nb)) if (!address_ok(regs, ea, nb))
return -EFAULT; return -EFAULT;
if ((ea & (nb - 1)) == 0) 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); return write_mem_unaligned(val, ea, nb, regs);
} }
NOKPROBE_SYMBOL(write_mem); NOKPROBE_SYMBOL(write_mem);
...@@ -422,7 +442,7 @@ static int do_fp_load(int rn, unsigned long ea, int nb, struct pt_regs *regs) ...@@ -422,7 +442,7 @@ static int do_fp_load(int rn, unsigned long ea, int nb, struct pt_regs *regs)
if (!address_ok(regs, ea, nb)) if (!address_ok(regs, ea, nb))
return -EFAULT; return -EFAULT;
err = copy_mem_in(u.b, ea, nb); err = copy_mem_in(u.b, ea, nb, regs);
if (err) if (err)
return err; return err;
preempt_disable(); preempt_disable();
...@@ -471,7 +491,7 @@ static int do_fp_store(int rn, unsigned long ea, int nb, struct pt_regs *regs) ...@@ -471,7 +491,7 @@ static int do_fp_store(int rn, unsigned long ea, int nb, struct pt_regs *regs)
u.l[1] = current->thread.TS_FPR(rn); u.l[1] = current->thread.TS_FPR(rn);
} }
preempt_enable(); preempt_enable();
return copy_mem_out(u.b, ea, nb); return copy_mem_out(u.b, ea, nb, regs);
} }
NOKPROBE_SYMBOL(do_fp_store); NOKPROBE_SYMBOL(do_fp_store);
#endif #endif
...@@ -491,7 +511,7 @@ static nokprobe_inline int do_vec_load(int rn, unsigned long ea, ...@@ -491,7 +511,7 @@ static nokprobe_inline int do_vec_load(int rn, unsigned long ea,
return -EFAULT; return -EFAULT;
/* align to multiple of size */ /* align to multiple of size */
ea &= ~(size - 1); 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) if (err)
return err; return err;
...@@ -523,7 +543,7 @@ static nokprobe_inline int do_vec_store(int rn, unsigned long ea, ...@@ -523,7 +543,7 @@ static nokprobe_inline int do_vec_store(int rn, unsigned long ea,
else else
u.v = current->thread.vr_state.vr[rn]; u.v = current->thread.vr_state.vr[rn];
preempt_enable(); 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 */ #endif /* CONFIG_ALTIVEC */
...@@ -725,7 +745,7 @@ static nokprobe_inline int do_vsx_load(struct instruction_op *op, ...@@ -725,7 +745,7 @@ static nokprobe_inline int do_vsx_load(struct instruction_op *op,
union vsx_reg buf; union vsx_reg buf;
int size = GETSIZE(op->type); 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; return -EFAULT;
emulate_vsx_load(op, &buf, mem); emulate_vsx_load(op, &buf, mem);
...@@ -776,7 +796,7 @@ static nokprobe_inline int do_vsx_store(struct instruction_op *op, ...@@ -776,7 +796,7 @@ static nokprobe_inline int do_vsx_store(struct instruction_op *op,
} }
preempt_enable(); preempt_enable();
emulate_vsx_store(op, &buf, mem); emulate_vsx_store(op, &buf, mem);
return copy_mem_out(mem, ea, size); return copy_mem_out(mem, ea, size, regs);
} }
#endif /* CONFIG_VSX */ #endif /* CONFIG_VSX */
...@@ -797,9 +817,11 @@ int emulate_dcbz(unsigned long ea, struct pt_regs *regs) ...@@ -797,9 +817,11 @@ int emulate_dcbz(unsigned long ea, struct pt_regs *regs)
return -EFAULT; return -EFAULT;
for (i = 0; i < size; i += sizeof(long)) { for (i = 0; i < size; i += sizeof(long)) {
err = __put_user(0, (unsigned long __user *) (ea + i)); err = __put_user(0, (unsigned long __user *) (ea + i));
if (err) if (err) {
regs->dar = ea;
return err; return err;
} }
}
return 0; return 0;
} }
NOKPROBE_SYMBOL(emulate_dcbz); NOKPROBE_SYMBOL(emulate_dcbz);
...@@ -2640,8 +2662,10 @@ int emulate_step(struct pt_regs *regs, unsigned int instr) ...@@ -2640,8 +2662,10 @@ int emulate_step(struct pt_regs *regs, unsigned int instr)
err = emulate_dcbz(ea, regs); err = emulate_dcbz(ea, regs);
break; break;
} }
if (err) if (err) {
regs->dar = ea;
return 0; return 0;
}
goto instr_done; goto instr_done;
case LARX: case LARX:
...@@ -2668,12 +2692,16 @@ int emulate_step(struct pt_regs *regs, unsigned int instr) ...@@ -2668,12 +2692,16 @@ int emulate_step(struct pt_regs *regs, unsigned int instr)
break; break;
case 16: case 16:
err = do_lqarx(ea, &regs->gpr[op.reg]); err = do_lqarx(ea, &regs->gpr[op.reg]);
goto ldst_done; break;
#endif #endif
default: default:
return 0; return 0;
} }
if (!err) if (err) {
regs->dar = ea;
return 0;
}
if (size < 16)
regs->gpr[op.reg] = val; regs->gpr[op.reg] = val;
goto ldst_done; goto ldst_done;
...@@ -2711,6 +2739,8 @@ int emulate_step(struct pt_regs *regs, unsigned int instr) ...@@ -2711,6 +2739,8 @@ int emulate_step(struct pt_regs *regs, unsigned int instr)
regs->ccr = (regs->ccr & 0x0fffffff) | regs->ccr = (regs->ccr & 0x0fffffff) |
(cr & 0xe0000000) | (cr & 0xe0000000) |
((regs->xer >> 3) & 0x10000000); ((regs->xer >> 3) & 0x10000000);
else
regs->dar = ea;
goto ldst_done; goto ldst_done;
case LOAD: case LOAD:
......
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