RFC: SPE misalignment handling framework
Kumar Gala
galak at kernel.crashing.org
Thu Aug 23 07:32:54 EST 2007
This is just to see if the modification to fix_alignment is ok with
people. Still need to fill in the actual implementation of emulate_spe().
diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c
index 4c47f9c..a9fcf25 100644
--- a/arch/powerpc/kernel/align.c
+++ b/arch/powerpc/kernel/align.c
@@ -189,6 +189,76 @@ static struct aligninfo aligninfo[128] = {
INVALID, /* 11 1 1111 */
};
+#ifdef CONFIG_SPE
+static struct aligninfo spe_aligninfo[64] = {
+ { 8, LD }, /* 0 00 00 0: evlddx */
+ { 8, LD }, /* 0 00 00 1: evldd */
+ { 8, LD }, /* 0 00 01 0: evldwx */
+ { 8, LD }, /* 0 00 01 1: evldw */
+ { 8, LD }, /* 0 00 10 0: evldhx */
+ { 8, LD }, /* 0 00 10 1: evldh */
+ INVALID, /* 0 00 11 0 */
+ INVALID, /* 0 00 11 1 */
+ { 2, LD }, /* 0 01 00 0: evlhhesplatx */
+ { 2, LD }, /* 0 01 00 1: evlhhesplat */
+ INVALID, /* 0 01 01 0 */
+ INVALID, /* 0 01 01 1 */
+ { 2, LD }, /* 0 01 10 0: evlhhousplatx */
+ { 2, LD }, /* 0 01 10 1: evlhhousplat */
+ { 2, LD }, /* 0 01 11 0: evlhhossplatx */
+ { 2, LD }, /* 0 01 11 1: evlhhossplat */
+ { 4, LD }, /* 0 10 00 0: evlwhex */
+ { 4, LD }, /* 0 10 00 1: evlwhe */
+ INVALID, /* 0 10 01 0 */
+ INVALID, /* 0 10 01 1 */
+ { 4, LD }, /* 0 10 10 0: evlwhoux */
+ { 4, LD }, /* 0 10 10 1: evlwhou */
+ { 4, LD }, /* 0 10 11 0: evlwhosx */
+ { 4, LD }, /* 0 10 11 1: evlwhos */
+ { 4, LD }, /* 0 11 00 0: evlwwsplatx */
+ { 4, LD }, /* 0 11 00 1: evlwwsplat */
+ INVALID, /* 0 11 01 0 */
+ INVALID, /* 0 11 01 1 */
+ { 4, LD }, /* 0 11 10 0: evlwhsplatx */
+ { 4, LD }, /* 0 11 10 1: evlwhsplat */
+ INVALID, /* 0 11 11 0 */
+ INVALID, /* 0 11 11 1 */
+
+ { 8, ST }, /* 1 00 00 0: evstddx */
+ { 8, ST }, /* 1 00 00 1: evstdd */
+ { 8, ST }, /* 1 00 01 0: evstdwx */
+ { 8, ST }, /* 1 00 01 1: evstdw */
+ { 8, ST }, /* 1 00 10 0: evstdhx */
+ { 8, ST }, /* 1 00 10 1: evstdh */
+ INVALID, /* 1 00 11 0 */
+ INVALID, /* 1 00 11 1 */
+ INVALID, /* 1 01 00 0 */
+ INVALID, /* 1 01 00 1 */
+ INVALID, /* 1 01 01 0 */
+ INVALID, /* 1 01 01 1 */
+ INVALID, /* 1 01 10 0 */
+ INVALID, /* 1 01 10 1 */
+ INVALID, /* 1 01 11 0 */
+ INVALID, /* 1 01 11 1 */
+ { 4, ST }, /* 1 10 00 0: evstwhex */
+ { 4, ST }, /* 1 10 00 1: evstwhe */
+ INVALID, /* 1 10 01 0 */
+ INVALID, /* 1 10 01 1 */
+ { 4, ST }, /* 1 10 10 0: evstwhoux */
+ { 4, ST }, /* 1 10 10 1: evstwhou */
+ INVALID, /* 1 10 11 0 */
+ INVALID, /* 1 10 11 1 */
+ { 4, ST }, /* 1 11 00 0: evstwwex */
+ { 4, ST }, /* 1 11 00 1: evstwwe */
+ INVALID, /* 1 11 01 0 */
+ INVALID, /* 1 11 01 1 */
+ { 4, ST }, /* 1 11 10 0: evstwwox */
+ { 4, ST }, /* 1 11 10 1: evstwwo */
+ INVALID, /* 1 11 11 0 */
+ INVALID, /* 1 11 11 1 */
+};
+#endif
+
/*
* Create a DSISR value from the instruction
*/
@@ -392,6 +462,103 @@ static int emulate_fp_pair(struct pt_regs *regs, unsigned char __user *addr,
return 1; /* exception handled and fixed up */
}
+#ifdef CONFIG_SPE
+/*
+ * Emulate SPE loads and stores.
+ * Only Book-E has these instructions, and it does true little-endian,
+ * so we don't need the address swizzling.
+ */
+static int emulate_spe(struct pt_regs *regs, unsigned char __user *addr,
+ unsigned int reg, unsigned int nb,
+ unsigned int flags, unsigned int instr)
+{
+ int i, ret;
+
+ /* userland only */
+ if (unlikely(!user_mode(regs)))
+ return 0;
+
+ flush_spe_to_thread(current);
+
+
+ return 1;
+}
+#endif /* CONFIG_SPE */
/*
* Called on alignment exception. Attempts to fixup
@@ -408,7 +575,7 @@ int fix_alignment(struct pt_regs *regs)
unsigned int dsisr;
unsigned char __user *addr;
unsigned long p, swiz;
- int ret, t;
+ int ret, t, is_spe = 0;
union {
u64 ll;
double dd;
@@ -445,17 +612,31 @@ int fix_alignment(struct pt_regs *regs)
if (cpu_has_feature(CPU_FTR_REAL_LE) && (regs->msr & MSR_LE))
instr = cpu_to_le32(instr);
dsisr = make_dsisr(instr);
+#ifdef CONFIG_SPE
+ if ((instr >> 26) == 0x4)
+ is_spe = 1;
+#endif
}
/* extract the operation and registers from the dsisr */
reg = (dsisr >> 5) & 0x1f; /* source/dest register */
areg = dsisr & 0x1f; /* register to update */
- instr = (dsisr >> 10) & 0x7f;
- instr |= (dsisr >> 13) & 0x60;
+ if (!is_spe) {
+ instr = (dsisr >> 10) & 0x7f;
+ instr |= (dsisr >> 13) & 0x60;
- /* Lookup the operation in our table */
- nb = aligninfo[instr].len;
- flags = aligninfo[instr].flags;
+ /* Lookup the operation in our table */
+ nb = aligninfo[instr].len;
+ flags = aligninfo[instr].flags;
+ }
+#ifdef CONFIG_SPE
+ else {
+ instr &= 0x3f;
+ /* Lookup the operation in our table */
+ nb = spe_aligninfo[instr].len;
+ flags = spe_aligninfo[instr].flags;
+ }
+#endif
/* Byteswap little endian loads and stores */
swiz = 0;
@@ -507,6 +688,14 @@ int fix_alignment(struct pt_regs *regs)
flush_fp_to_thread(current);
}
+#ifdef CONFIG_SPE
+ /* Force the fprs into the save area so we can reference them */
+ if (is_spe) {
+ return emulate_spe(regs, addr, reg, nb, flags, instr);
+
+ }
+#endif
+
/* Special case for 16-byte FP loads and stores */
if (nb == 16)
return emulate_fp_pair(regs, addr, reg, flags);
More information about the Linuxppc-dev
mailing list