[Linux-ia64] [patch] fix unaligned references inside s/w pipelined loops

Luck, Tony
2001-10-16T15:54:08Z

[-- Attachment #1: Type: text/plain, Size: 655 bytes --]

If an application takes an unaligned trap on a "rotating"
register inside a software pipelined loop[1], then the kernel
will use the wrong register when it fixes the fault. This
results in corrupted memory or register depending on whether
the unaligned reference was a store or a load respectively.

Attached is a patch to fix this (both for the integer case,
which was the one actually reported to me, and the floating
point case too). Patch is against 2.4.10.

-Tony Luck

[1] Don't ask me why someone would go through all the effort
of writing a s/w pipelined loop, but not check for unaligned
access, I don't understand it either ... but they did.

[-- Attachment #2: diff.unaligned.txt --]
[-- Type: text/plain, Size: 3957 bytes --]

--- ../../REF/2.4.10-ia64-combo/arch/ia64/kernel/unaligned.c Wed Oct 3 11:02:18 2001
+++ linux/arch/ia64/kernel/unaligned.c Thu Oct 11 09:00:54 2001
@@ -5,6 +5,7 @@
* Copyright (C) 1999-2000 Stephane Eranian <eranian@hpl.hp.com>
* Copyright (C) 2001 David Mosberger-Tang <davidm@hpl.hp.com>
*
+ * 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/01/17 Add support emulation of unaligned kernel accesses.
*/
@@ -283,9 +284,19 @@
unsigned long rnats, nat_mask;
unsigned long on_kbs;
long sof = (regs->cr_ifs) & 0x7f;
+ long sor = 8 * ((regs->cr_ifs >> 14) & 0xf);
+ long rrb_gr = (regs->cr_ifs >> 18) & 0x7f;
+ long ridx;
+
+ 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\n",
- r1, sw->ar_bspstore, regs->ar_bspstore, sof, (regs->cr_ifs >> 7) & 0x7f);
+ 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) {
/* this should never happen, as the "rsvd register fault" has higher priority */
@@ -294,7 +305,7 @@
}

on_kbs = ia64_rse_num_regs(kbs, (unsigned long *) sw->ar_bspstore);
- addr = ia64_rse_skip_regs((unsigned long *) sw->ar_bspstore, -sof + (r1 - 32));
+ addr = ia64_rse_skip_regs((unsigned long *) sw->ar_bspstore, ridx);
if (addr >= kbs) {
/* the register is on the kernel backing store: easy... */
rnat_addr = ia64_rse_rnat_addr(addr);
@@ -319,12 +330,12 @@
return;
}

- bspstore = (unsigned long *) regs->ar_bspstore;
+ bspstore = (unsigned long *)regs->ar_bspstore;
ubs_end = ia64_rse_skip_regs(bspstore, on_kbs);
bsp = ia64_rse_skip_regs(ubs_end, -sof);
- addr = ia64_rse_skip_regs(bsp, r1 - 32);
+ addr = ia64_rse_skip_regs(bsp, ridx + sof);

- DPRINT("ubs_end=%p bsp=%p addr=%px\n", (void *) ubs_end, (void *) bsp, (void *) addr);
+ DPRINT("ubs_end=%p bsp=%p addr=%p\n", (void *) ubs_end, (void *) bsp, (void *) addr);

ia64_poke(current, sw, (unsigned long) ubs_end, (unsigned long) addr, val);

@@ -354,9 +365,19 @@
unsigned long rnats, nat_mask;
unsigned long on_kbs;
long sof = (regs->cr_ifs) & 0x7f;
+ long sor = 8 * ((regs->cr_ifs >> 14) & 0xf);
+ long rrb_gr = (regs->cr_ifs >> 18) & 0x7f;
+ long ridx;
+
+ 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\n",
- r1, sw->ar_bspstore, regs->ar_bspstore, sof, (regs->cr_ifs >> 7) & 0x7f);
+ 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) {
/* this should never happen, as the "rsvd register fault" has higher priority */
@@ -365,7 +386,7 @@
}

on_kbs = ia64_rse_num_regs(kbs, (unsigned long *) sw->ar_bspstore);
- addr = ia64_rse_skip_regs((unsigned long *) sw->ar_bspstore, -sof + (r1 - 32));
+ addr = ia64_rse_skip_regs((unsigned long *) sw->ar_bspstore, ridx);
if (addr >= kbs) {
/* the register is on the kernel backing store: easy... */
*val = *addr;
@@ -391,7 +412,7 @@
bspstore = (unsigned long *)regs->ar_bspstore;
ubs_end = ia64_rse_skip_regs(bspstore, on_kbs);
bsp = ia64_rse_skip_regs(ubs_end, -sof);
- addr = ia64_rse_skip_regs(bsp, r1 - 32);
+ addr = ia64_rse_skip_regs(bsp, ridx + sof);

DPRINT("ubs_end=%p bsp=%p addr=%p\n", (void *) ubs_end, (void *) bsp, (void *) addr);