Commit 2c5db17f authored by David Mosberger's avatar David Mosberger

ia64: Fix unaligned memory access handler.

parent 845eaa9b
...@@ -5,6 +5,10 @@ ...@@ -5,6 +5,10 @@
* Stephane Eranian <eranian@hpl.hp.com> * Stephane Eranian <eranian@hpl.hp.com>
* David Mosberger-Tang <davidm@hpl.hp.com> * David Mosberger-Tang <davidm@hpl.hp.com>
* *
* 2002/12/09 Fix rotating register handling (off-by-1 error, missing fr-rotation). Fix
* get_rse_reg() to not leak kernel bits to user-level (reading an out-of-frame
* stacked register returns an undefined value; it does NOT trigger a
* "rsvd register fault").
* 2001/10/11 Fix unaligned access to rotating registers in s/w pipelined loops. * 2001/10/11 Fix unaligned access to rotating registers in s/w pipelined loops.
* 2001/08/13 Correct size of extended floats (float_fsz) from 16 to 10 bytes. * 2001/08/13 Correct size of extended floats (float_fsz) from 16 to 10 bytes.
* 2001/01/17 Add support emulation of unaligned kernel accesses. * 2001/01/17 Add support emulation of unaligned kernel accesses.
...@@ -276,6 +280,15 @@ invala_fr (int regno) ...@@ -276,6 +280,15 @@ invala_fr (int regno)
# undef F # undef F
} }
static inline unsigned long
rotate_reg (unsigned long sor, unsigned long rrb, unsigned long reg)
{
reg += rrb;
if (reg >= sor)
reg -= sor;
return reg;
}
static void static void
set_rse_reg (struct pt_regs *regs, unsigned long r1, unsigned long val, int nat) set_rse_reg (struct pt_regs *regs, unsigned long r1, unsigned long val, int nat)
{ {
...@@ -287,26 +300,22 @@ set_rse_reg (struct pt_regs *regs, unsigned long r1, unsigned long val, int nat) ...@@ -287,26 +300,22 @@ set_rse_reg (struct pt_regs *regs, unsigned long r1, unsigned long val, int nat)
long sof = (regs->cr_ifs) & 0x7f; long sof = (regs->cr_ifs) & 0x7f;
long sor = 8 * ((regs->cr_ifs >> 14) & 0xf); long sor = 8 * ((regs->cr_ifs >> 14) & 0xf);
long rrb_gr = (regs->cr_ifs >> 18) & 0x7f; long rrb_gr = (regs->cr_ifs >> 18) & 0x7f;
long ridx; long ridx = r1 - 32;
if ((r1 - 32) > sor)
ridx = -sof + (r1 - 32);
else if ((r1 - 32) < (sor - rrb_gr))
ridx = -sof + (r1 - 32) + rrb_gr;
else
ridx = -sof + (r1 - 32) - (sor - rrb_gr);
DPRINT("r%lu, sw.bspstore=%lx pt.bspstore=%lx sof=%ld sol=%ld ridx=%ld\n",
r1, sw->ar_bspstore, regs->ar_bspstore, sof, (regs->cr_ifs >> 7) & 0x7f, ridx);
if ((r1 - 32) >= sof) { if (ridx >= sof) {
/* this should never happen, as the "rsvd register fault" has higher priority */ /* this should never happen, as the "rsvd register fault" has higher priority */
DPRINT("ignoring write to r%lu; only %lu registers are allocated!\n", r1, sof); DPRINT("ignoring write to r%lu; only %lu registers are allocated!\n", r1, sof);
return; return;
} }
if (ridx < sor)
ridx = rotate_reg(sor, rrb_gr, ridx);
DPRINT("r%lu, sw.bspstore=%lx pt.bspstore=%lx sof=%ld sol=%ld ridx=%ld\n",
r1, sw->ar_bspstore, regs->ar_bspstore, sof, (regs->cr_ifs >> 7) & 0x7f, ridx);
on_kbs = ia64_rse_num_regs(kbs, (unsigned long *) sw->ar_bspstore); on_kbs = ia64_rse_num_regs(kbs, (unsigned long *) sw->ar_bspstore);
addr = ia64_rse_skip_regs((unsigned long *) sw->ar_bspstore, ridx); addr = ia64_rse_skip_regs((unsigned long *) sw->ar_bspstore, -sof + ridx);
if (addr >= kbs) { if (addr >= kbs) {
/* the register is on the kernel backing store: easy... */ /* the register is on the kernel backing store: easy... */
rnat_addr = ia64_rse_rnat_addr(addr); rnat_addr = ia64_rse_rnat_addr(addr);
...@@ -334,7 +343,7 @@ set_rse_reg (struct pt_regs *regs, unsigned long r1, unsigned long val, int nat) ...@@ -334,7 +343,7 @@ set_rse_reg (struct pt_regs *regs, unsigned long r1, unsigned long val, int nat)
bspstore = (unsigned long *)regs->ar_bspstore; bspstore = (unsigned long *)regs->ar_bspstore;
ubs_end = ia64_rse_skip_regs(bspstore, on_kbs); ubs_end = ia64_rse_skip_regs(bspstore, on_kbs);
bsp = ia64_rse_skip_regs(ubs_end, -sof); bsp = ia64_rse_skip_regs(ubs_end, -sof);
addr = ia64_rse_skip_regs(bsp, ridx + sof); addr = ia64_rse_skip_regs(bsp, ridx);
DPRINT("ubs_end=%p bsp=%p addr=%p\n", (void *) ubs_end, (void *) bsp, (void *) addr); DPRINT("ubs_end=%p bsp=%p addr=%p\n", (void *) ubs_end, (void *) bsp, (void *) addr);
...@@ -368,26 +377,22 @@ get_rse_reg (struct pt_regs *regs, unsigned long r1, unsigned long *val, int *na ...@@ -368,26 +377,22 @@ get_rse_reg (struct pt_regs *regs, unsigned long r1, unsigned long *val, int *na
long sof = (regs->cr_ifs) & 0x7f; long sof = (regs->cr_ifs) & 0x7f;
long sor = 8 * ((regs->cr_ifs >> 14) & 0xf); long sor = 8 * ((regs->cr_ifs >> 14) & 0xf);
long rrb_gr = (regs->cr_ifs >> 18) & 0x7f; long rrb_gr = (regs->cr_ifs >> 18) & 0x7f;
long ridx; long ridx = r1 - 32;
if ((r1 - 32) > sor) if (ridx >= sof) {
ridx = -sof + (r1 - 32); /* read of out-of-frame register returns an undefined value; 0 in our case. */
else if ((r1 - 32) < (sor - rrb_gr)) DPRINT("ignoring read from r%lu; only %lu registers are allocated!\n", r1, sof);
ridx = -sof + (r1 - 32) + rrb_gr; goto fail;
else }
ridx = -sof + (r1 - 32) - (sor - rrb_gr);
if (ridx < sor)
ridx = rotate_reg(sor, rrb_gr, ridx);
DPRINT("r%lu, sw.bspstore=%lx pt.bspstore=%lx sof=%ld sol=%ld ridx=%ld\n", DPRINT("r%lu, sw.bspstore=%lx pt.bspstore=%lx sof=%ld sol=%ld ridx=%ld\n",
r1, sw->ar_bspstore, regs->ar_bspstore, sof, (regs->cr_ifs >> 7) & 0x7f, ridx); r1, sw->ar_bspstore, regs->ar_bspstore, sof, (regs->cr_ifs >> 7) & 0x7f, ridx);
if ((r1 - 32) >= sof) {
/* this should never happen, as the "rsvd register fault" has higher priority */
DPRINT("ignoring read from r%lu; only %lu registers are allocated!\n", r1, sof);
return;
}
on_kbs = ia64_rse_num_regs(kbs, (unsigned long *) sw->ar_bspstore); on_kbs = ia64_rse_num_regs(kbs, (unsigned long *) sw->ar_bspstore);
addr = ia64_rse_skip_regs((unsigned long *) sw->ar_bspstore, ridx); addr = ia64_rse_skip_regs((unsigned long *) sw->ar_bspstore, -sof + ridx);
if (addr >= kbs) { if (addr >= kbs) {
/* the register is on the kernel backing store: easy... */ /* the register is on the kernel backing store: easy... */
*val = *addr; *val = *addr;
...@@ -407,13 +412,13 @@ get_rse_reg (struct pt_regs *regs, unsigned long r1, unsigned long *val, int *na ...@@ -407,13 +412,13 @@ get_rse_reg (struct pt_regs *regs, unsigned long r1, unsigned long *val, int *na
*/ */
if (regs->r12 >= TASK_SIZE) { if (regs->r12 >= TASK_SIZE) {
DPRINT("ignoring kernel read of r%lu; register isn't on the RBS!", r1); DPRINT("ignoring kernel read of r%lu; register isn't on the RBS!", r1);
return; goto fail;
} }
bspstore = (unsigned long *)regs->ar_bspstore; bspstore = (unsigned long *)regs->ar_bspstore;
ubs_end = ia64_rse_skip_regs(bspstore, on_kbs); ubs_end = ia64_rse_skip_regs(bspstore, on_kbs);
bsp = ia64_rse_skip_regs(ubs_end, -sof); bsp = ia64_rse_skip_regs(ubs_end, -sof);
addr = ia64_rse_skip_regs(bsp, ridx + sof); addr = ia64_rse_skip_regs(bsp, ridx);
DPRINT("ubs_end=%p bsp=%p addr=%p\n", (void *) ubs_end, (void *) bsp, (void *) addr); DPRINT("ubs_end=%p bsp=%p addr=%p\n", (void *) ubs_end, (void *) bsp, (void *) addr);
...@@ -428,6 +433,13 @@ get_rse_reg (struct pt_regs *regs, unsigned long r1, unsigned long *val, int *na ...@@ -428,6 +433,13 @@ get_rse_reg (struct pt_regs *regs, unsigned long r1, unsigned long *val, int *na
ia64_peek(current, sw, (unsigned long) ubs_end, (unsigned long) rnat_addr, &rnats); ia64_peek(current, sw, (unsigned long) ubs_end, (unsigned long) rnat_addr, &rnats);
*nat = (rnats & nat_mask) != 0; *nat = (rnats & nat_mask) != 0;
} }
return;
fail:
*val = 0;
if (nat)
*nat = 0;
return;
} }
...@@ -486,7 +498,16 @@ setreg (unsigned long regnum, unsigned long val, int nat, struct pt_regs *regs) ...@@ -486,7 +498,16 @@ setreg (unsigned long regnum, unsigned long val, int nat, struct pt_regs *regs)
DPRINT("*0x%lx=0x%lx NaT=%d new unat: %p=%lx\n", addr, val, nat, (void *) unat,*unat); DPRINT("*0x%lx=0x%lx NaT=%d new unat: %p=%lx\n", addr, val, nat, (void *) unat,*unat);
} }
#define IA64_FPH_OFFS(r) (r - IA64_FIRST_ROTATING_FR) /*
* Return the (rotated) index for floating point register REGNUM (REGNUM must be in the
* range from 32-127, result is in the range from 0-95.
*/
static inline unsigned long
fph_index (struct pt_regs *regs, long regnum)
{
unsigned long rrb_fr = (regs->cr_ifs >> 25) & 0x7f;
return rotate_reg(96, rrb_fr, (regnum - IA64_FIRST_ROTATING_FR));
}
static void static void
setfpreg (unsigned long regnum, struct ia64_fpreg *fpval, struct pt_regs *regs) setfpreg (unsigned long regnum, struct ia64_fpreg *fpval, struct pt_regs *regs)
...@@ -507,7 +528,7 @@ setfpreg (unsigned long regnum, struct ia64_fpreg *fpval, struct pt_regs *regs) ...@@ -507,7 +528,7 @@ setfpreg (unsigned long regnum, struct ia64_fpreg *fpval, struct pt_regs *regs)
*/ */
if (regnum >= IA64_FIRST_ROTATING_FR) { if (regnum >= IA64_FIRST_ROTATING_FR) {
ia64_sync_fph(current); ia64_sync_fph(current);
current->thread.fph[IA64_FPH_OFFS(regnum)] = *fpval; current->thread.fph[fph_index(regs, regnum)] = *fpval;
} else { } else {
/* /*
* pt_regs or switch_stack ? * pt_regs or switch_stack ?
...@@ -566,7 +587,7 @@ getfpreg (unsigned long regnum, struct ia64_fpreg *fpval, struct pt_regs *regs) ...@@ -566,7 +587,7 @@ getfpreg (unsigned long regnum, struct ia64_fpreg *fpval, struct pt_regs *regs)
*/ */
if (regnum >= IA64_FIRST_ROTATING_FR) { if (regnum >= IA64_FIRST_ROTATING_FR) {
ia64_flush_fph(current); ia64_flush_fph(current);
*fpval = current->thread.fph[IA64_FPH_OFFS(regnum)]; *fpval = current->thread.fph[fph_index(regs, regnum)];
} else { } else {
/* /*
* f0 = 0.0, f1= 1.0. Those registers are constant and are thus * f0 = 0.0, f1= 1.0. Those registers are constant and are thus
...@@ -651,7 +672,7 @@ emulate_load_updates (update_t type, load_store_t ld, struct pt_regs *regs, unsi ...@@ -651,7 +672,7 @@ emulate_load_updates (update_t type, load_store_t ld, struct pt_regs *regs, unsi
* just in case. * just in case.
*/ */
if (ld.x6_op == 1 || ld.x6_op == 3) { if (ld.x6_op == 1 || ld.x6_op == 3) {
printk("%s %s: register update on speculative load, error\n", KERN_ERR, __FUNCTION__); printk(KERN_ERR "%s: register update on speculative load, error\n", __FUNCTION__);
die_if_kernel("unaligned reference on specualtive load with register update\n", die_if_kernel("unaligned reference on specualtive load with register update\n",
regs, 30); regs, 30);
} }
...@@ -1081,8 +1102,8 @@ emulate_load_floatpair (unsigned long ifa, load_store_t ld, struct pt_regs *regs ...@@ -1081,8 +1102,8 @@ emulate_load_floatpair (unsigned long ifa, load_store_t ld, struct pt_regs *regs
* For this reason we keep this sanity check * For this reason we keep this sanity check
*/ */
if (ld.x6_op == 1 || ld.x6_op == 3) if (ld.x6_op == 1 || ld.x6_op == 3)
printk("%s %s: register update on speculative load pair, " printk(KERN_ERR "%s: register update on speculative load pair, error\n",
"error\n",KERN_ERR, __FUNCTION__); __FUNCTION__);
setreg(ld.r3, ifa, 0, regs); setreg(ld.r3, ifa, 0, regs);
} }
......
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