Would someone with an affected system (Multia or Jensen) try this out?
It removes two global variables, and avoids an extra test once we're
done with startup code.
r~
--- 2.4.15-7/arch/alpha/kernel/traps.c.~2~ Mon Nov 19 23:05:50 2001
+++ 2.4.15-7/arch/alpha/kernel/traps.c Mon Nov 19 23:07:32 2001
@@ -24,31 +24,32 @@
#include "proto.h"
-/* data/code implementing a work-around for some SRMs which
- mishandle opDEC faults
-*/
-static int opDEC_testing = 0;
-static int opDEC_fix = 0;
-static unsigned long opDEC_test_pc = 0;
+/* Work-around for some SRMs which mishandle opDEC faults. */
+static int opDEC_fix;
static void
opDEC_check(void)
{
- unsigned long test_pc;
+ unsigned long data;
- lock_kernel();
- opDEC_testing = 1;
+ /* We temporarily set opDEC_fix so that we skip forward to either
+ the cmpteq (for a broken srm) or the addt (for a working srm).
+ Because of this skip, at the end of the sequence $f0 will be 0
+ for a working srm, and 2.0 for a broken srm. */
- __asm__ __volatile__(
- " br %0,1f\n"
- "1: addq %0,8,%0\n"
- " stq %0,%1\n"
- " cvttq/svm $f31,$f31\n"
- : "=&r"(test_pc), "=m"(opDEC_test_pc)
- : );
+ opDEC_fix = 8;
- opDEC_testing = 0;
- unlock_kernel();
+ __asm__ __volatile__(
+ "fmov $f31, $f0\n\t"
+ "cvttq/svm $f31, $f31\n\t"
+ "cmpteq $f31, $f31, $f0\n\t"
+ "addt $f31, $f31, $f31\n\t"
+ "stt $f0, %0"
+ : "=m"(data));
+
+ opDEC_fix = data ? 4 : 0;
+ if (data)
+ printk("opDEC fixup enabled.\n");
}
void
@@ -231,24 +232,22 @@ do_entIF(unsigned long type, unsigned lo
unsigned long a2, unsigned long a3, unsigned long a4,
unsigned long a5, struct pt_regs regs)
{
- if (!opDEC_testing || type != 4) {
- die_if_kernel((type == 1 ? "Kernel Bug" : "Instruction fault"),
- ®s, type, 0);
- }
-
switch (type) {
- case 0: /* breakpoint */
+ case 0: /* breakpoint */
+ die_if_kernel("Breakpoint fault", ®s, type, 0);
if (ptrace_cancel_bpt(current)) {
regs.pc -= 4; /* make pc point to former bpt */
}
send_sig(SIGTRAP, current, 1);
return;
- case 1: /* bugcheck */
+ case 1: /* bugcheck */
+ die_if_kernel("Kernel Bug", ®s, type, 0);
send_sig(SIGTRAP, current, 1);
return;
- case 2: /* gentrap */
+ case 2: /* gentrap */
+ die_if_kernel("Divide by zero fault", ®s, type, 0);
/*
* The exception code should be passed on to the signal
* handler as the second argument. Linux doesn't do that
@@ -285,27 +284,14 @@ do_entIF(unsigned long type, unsigned lo
}
break;
- case 4: /* opDEC */
+ case 4: /* opDEC */
if (implver() == IMPLVER_EV4) {
- /* The some versions of SRM do not handle
- the opDEC properly - they return the PC of the
- opDEC fault, not the instruction after as the
- Alpha architecture requires. Here we fix it up.
- We do this by intentionally causing an opDEC
- fault during the boot sequence and testing if
- we get the correct PC. If not, we set a flag
- to correct it every time through.
- */
- if (opDEC_testing) {
- if (regs.pc == opDEC_test_pc) {
- opDEC_fix = 4;
- regs.pc += 4;
- printk("opDEC fixup enabled.\n");
- }
- return;
- }
+ /* Some versions of SRM do not handle opDEC properly.
+ They return the PC of the opDEC fault, not the
+ instruction after the fault as the architecture
+ requires. Here we fix it up. */
regs.pc += opDEC_fix;
-
+
/* EV4 does not implement anything except normal
rounding. Everything else will come here as
an illegal instruction. Emulate them. */
@@ -314,7 +300,7 @@ do_entIF(unsigned long type, unsigned lo
}
break;
- case 3: /* FEN fault */
+ case 3: /* FEN fault */
/* Irritating users can call PAL_clrfen to disable the
FPU for the process. The kernel will then trap in
do_switch_stack and undo_switch_stack when we try
@@ -328,10 +314,12 @@ do_entIF(unsigned long type, unsigned lo
__reload_thread(¤t->thread);
return;
- case 5: /* illoc */
- default: /* unexpected instruction-fault type */
- ;
+ case 5: /* illoc */
+ default: /* unexpected instruction-fault type */
+ break;
}
+
+ die_if_kernel("Instruction fault", ®s, type, 0);
send_sig(SIGILL, current, 1);
}
@@ -1014,9 +1002,7 @@ trap_init(void)
wrent(entDbg, 6);
/* Hack for Multia (UDB) and JENSEN: some of their SRMs have
- * a bug in the handling of the opDEC fault. Fix it up if so.
- */
- if (implver() == IMPLVER_EV4) {
+ a bug in the handling of the opDEC fault. Fix it up if so. */
+ if (implver() == IMPLVER_EV4)
opDEC_check();
- }
}
On Mon, Nov 19, 2001 at 11:23:55PM -0800, Richard Henderson wrote:
> --- 2.4.15-7/arch/alpha/kernel/traps.c.~2~ Mon Nov 19 23:05:50 2001
> +++ 2.4.15-7/arch/alpha/kernel/traps.c Mon Nov 19 23:07:32 2001
It seems to be the wrong diff:
patching file arch/alpha/kernel/traps.c
Hunk #1 succeeded at 23 (offset -1 lines).
Hunk #3 succeeded at 283 (offset -1 lines).
Hunk #4 FAILED at 299.
Hunk #5 FAILED at 313.
Hunk #6 succeeded at 988 (offset -14 lines).
2 out of 6 hunks FAILED -- saving rejects to file arch/alpha/kernel/traps.c.rej
Ivan.
On Tue, Nov 20, 2001 at 01:31:50PM +0300, Ivan Kokshaysky wrote:
> On Mon, Nov 19, 2001 at 11:23:55PM -0800, Richard Henderson wrote:
> > --- 2.4.15-7/arch/alpha/kernel/traps.c.~2~ Mon Nov 19 23:05:50 2001
> > +++ 2.4.15-7/arch/alpha/kernel/traps.c Mon Nov 19 23:07:32 2001
>
> It seems to be the wrong diff:
Oh, it depended on this one.
r~
--- 2.4.15-7/arch/alpha/kernel/traps.c.~1~ Mon Nov 19 23:03:26 2001
+++ 2.4.15-7/arch/alpha/kernel/traps.c Mon Nov 19 23:05:50 2001
@@ -20,6 +20,7 @@
#include <asm/unaligned.h>
#include <asm/sysinfo.h>
#include <asm/hwrpb.h>
+#include <asm/mmu_context.h>
#include "proto.h"
@@ -311,8 +312,22 @@ do_entIF(unsigned long type, unsigned lo
if (alpha_fp_emul(regs.pc-4))
return;
}
- /* fallthrough as illegal instruction .. */
+ break;
+
case 3: /* FEN fault */
+ /* Irritating users can call PAL_clrfen to disable the
+ FPU for the process. The kernel will then trap in
+ do_switch_stack and undo_switch_stack when we try
+ to save and restore the FP registers.
+
+ Given that GCC by default generates code that uses the
+ FP registers, PAL_clrfen is not useful except for DoS
+ attacks. So turn the bleeding FPU back on and be done
+ with it. */
+ current->thread.pal_flags |= 1;
+ __reload_thread(¤t->thread);
+ return;
+
case 5: /* illoc */
default: /* unexpected instruction-fault type */
;
On Tue, Nov 20, 2001 at 09:08:18AM -0800, Richard Henderson wrote:
> Oh, it depended on this one.
Ok, but with opDEC_fix = 8 we actually skip to addt/stt, so that asm
probably should be rearranged to
"fmov $f31, $f0\n\t"
"cvttq/svm $f31, $f31\n\t"
"addt $f31, $f31, $f31\n\t"
"cmpteq $f31, $f31, $f0\n\t"
"stt $f0, %0"
Further, if fp emulation isn't compiled in, we'll have kernel mode
instruction fault. A quick fix appears to be
regs.pc += opDEC_fix;
+ if (opDEC_fix == 8)
+ return;
Did I missed something?
Ivan.
On Tue, Nov 20, 2001 at 08:51:05PM +0300, Ivan Kokshaysky wrote:
> Ok, but with opDEC_fix = 8 we actually skip to addt/stt, so that asm
> probably should be rearranged to ...
No, read the comment and think about it some more.
"fmov $f31, $f0\n\t"
"cvttq/svm $f31, $f31\n\t"
"cmpteq $f31, $f31, $f0\n\t"
"addt $f31, $f31, $f31\n\t"
"stt $f0, %0"
In the broken case, we'll enter entIF with pc==cvttq. Add 8 gives
addt, subtract 4 gives cmpteq, which gives $f0 = 2.0.
In the working case, we'll enter entIF with pc==cmpteq. Add 8 gives
stt, subtract 4 gives addt, which is engineered to be a noop.
> Further, if fp emulation isn't compiled in, we'll have kernel mode
> instruction fault. A quick fix appears to be
Hmm. If fp emulation isn't compiled in, we shouldn't bother
testing this, I think. Means you can't debug fp emulation via
modules on Multia, but I'm pretty sure I don't care.
I suppose the other alternative to get the testing code out of
the normal entIF is to create a custom entIF that is installed
only during opDEC testing. Seems too much work...
r~
On Tue, Nov 20, 2001 at 10:47:48AM -0800, Richard Henderson wrote:
> In the working case, we'll enter entIF with pc==cmpteq. Add 8 gives
> stt, subtract 4 gives addt, which is engineered to be a noop.
Argh. I was thinking too much about dummy_emul case and missed the fact
that instruction at pc-4 is actually executed in alpha_fp_emul()...
> Hmm. If fp emulation isn't compiled in, we shouldn't bother
> testing this, I think. Means you can't debug fp emulation via
> modules on Multia, but I'm pretty sure I don't care.
Yes, making opDEC_check stuff `#ifdef CONFIG_MATHEMU' would be
reasonable.
> I suppose the other alternative to get the testing code out of
> the normal entIF is to create a custom entIF that is installed
> only during opDEC testing. Seems too much work...
Agreed. Alternatively, it's possible to hack dummy_emul(), which
doesn't affect the normal case.
static long dummy_emul(unsigned long pc)
{
if (opDEC_fix != 8)
return 0;
/* Trap in opDEC_check() */
if (*(u32 *)pc == 0x5bff141f) /* addt $f31, $f31, $f31 */
return 1; /* SRM updates PC correctly */
/* Broken SRM. "Emulate" cmpteq in opDEC_check() */
__asm__ __volatile__("cmpteq $f31, $f31, $f0\n");
return 1;
}
Ivan.
On Wed, Nov 21, 2001 at 03:15:55PM +0300, Ivan Kokshaysky wrote:
> > I suppose the other alternative to get the testing code out of
> > the normal entIF is to create a custom entIF that is installed
> > only during opDEC testing. Seems too much work...
>
> Agreed. Alternatively, it's possible to hack dummy_emul(), which
> doesn't affect the normal case.
Actually, the custom entIF isn't that much work. What about this?
r~
--- traps.c.orig Wed Nov 21 14:13:31 2001
+++ traps.c Wed Nov 21 14:25:22 2001
@@ -24,31 +24,35 @@
#include "proto.h"
-/* data/code implementing a work-around for some SRMs which
- mishandle opDEC faults
-*/
-static int opDEC_testing = 0;
-static int opDEC_fix = 0;
-static unsigned long opDEC_test_pc = 0;
+/* Work-around for some SRMs which mishandle opDEC faults. */
+static int opDEC_fix;
-static void
+extern void opDEC_check_entIF(void);
+asm(".section .text.init,\"ax\" \n\
+ .ent opDEC_check_entIF \n\
+opDEC_check_entIF: \n\
+ ldq $16,8($sp) \n\
+ call_pal 63 /* PAL_rti */ \n\
+ .end opDEC_check_entIF \n\
+ .previous");
+
+static void __init
opDEC_check(void)
{
- unsigned long test_pc;
+ long diff;
- lock_kernel();
- opDEC_testing = 1;
+ wrent(opDEC_check_entIF, 3);
__asm__ __volatile__(
- " br %0,1f\n"
- "1: addq %0,8,%0\n"
- " stq %0,%1\n"
- " cvttq/svm $f31,$f31\n"
- : "=&r"(test_pc), "=m"(opDEC_test_pc)
- : );
-
- opDEC_testing = 0;
- unlock_kernel();
+ "br %0,1f\n"
+ "1:\n\t"
+ "cvttq/svm $f31, $f31\n\t"
+ "subq $16, %0, %0"
+ : "=r"(diff) : : "$16");
+
+ opDEC_fix = 4 - diff;
+ if (opDEC_fix)
+ printk("opDEC fixup enabled.\n");
}
void
@@ -231,24 +235,22 @@
unsigned long a2, unsigned long a3, unsigned long a4,
unsigned long a5, struct pt_regs regs)
{
- if (!opDEC_testing || type != 4) {
- die_if_kernel((type == 1 ? "Kernel Bug" : "Instruction fault"),
- ®s, type, 0);
- }
-
switch (type) {
- case 0: /* breakpoint */
+ case 0: /* breakpoint */
+ die_if_kernel("Breakpoint fault", ®s, type, 0);
if (ptrace_cancel_bpt(current)) {
regs.pc -= 4; /* make pc point to former bpt */
}
send_sig(SIGTRAP, current, 1);
return;
- case 1: /* bugcheck */
+ case 1: /* bugcheck */
+ die_if_kernel("Kernel Bug", ®s, type, 0);
send_sig(SIGTRAP, current, 1);
return;
- case 2: /* gentrap */
+ case 2: /* gentrap */
+ die_if_kernel("Divide by zero fault", ®s, type, 0);
/*
* The exception code should be passed on to the signal
* handler as the second argument. Linux doesn't do that
@@ -285,27 +287,14 @@
}
break;
- case 4: /* opDEC */
+ case 4: /* opDEC */
if (implver() == IMPLVER_EV4) {
- /* The some versions of SRM do not handle
- the opDEC properly - they return the PC of the
- opDEC fault, not the instruction after as the
- Alpha architecture requires. Here we fix it up.
- We do this by intentionally causing an opDEC
- fault during the boot sequence and testing if
- we get the correct PC. If not, we set a flag
- to correct it every time through.
- */
- if (opDEC_testing) {
- if (regs.pc == opDEC_test_pc) {
- opDEC_fix = 4;
- regs.pc += 4;
- printk("opDEC fixup enabled.\n");
- }
- return;
- }
+ /* Some versions of SRM do not handle opDEC properly.
+ They return the PC of the opDEC fault, not the
+ instruction after the fault as the architecture
+ requires. Here we fix it up. */
regs.pc += opDEC_fix;
-
+
/* EV4 does not implement anything except normal
rounding. Everything else will come here as
an illegal instruction. Emulate them. */
@@ -314,7 +303,7 @@
}
break;
- case 3: /* FEN fault */
+ case 3: /* FEN fault */
/* Irritating users can call PAL_clrfen to disable the
FPU for the process. The kernel will then trap in
do_switch_stack and undo_switch_stack when we try
@@ -328,10 +317,12 @@
__reload_thread(¤t->thread);
return;
- case 5: /* illoc */
- default: /* unexpected instruction-fault type */
- ;
+ case 5: /* illoc */
+ default: /* unexpected instruction-fault type */
+ break;
}
+
+ die_if_kernel("Instruction fault", ®s, type, 0);
send_sig(SIGILL, current, 1);
}
@@ -999,24 +990,22 @@
return -ENOSYS;
}
-void
+void __init
trap_init(void)
{
/* Tell PAL-code what global pointer we want in the kernel. */
register unsigned long gptr __asm__("$29");
wrkgp(gptr);
+ /* Hack for Multia (UDB) and JENSEN: some of their SRMs have
+ a bug in the handling of the opDEC fault. Fix it up if so. */
+ if (implver() == IMPLVER_EV4)
+ opDEC_check();
+
wrent(entArith, 1);
wrent(entMM, 2);
wrent(entIF, 3);
wrent(entUna, 4);
wrent(entSys, 5);
wrent(entDbg, 6);
-
- /* Hack for Multia (UDB) and JENSEN: some of their SRMs have
- * a bug in the handling of the opDEC fault. Fix it up if so.
- */
- if (implver() == IMPLVER_EV4) {
- opDEC_check();
- }
}
On Wed, Nov 21, 2001 at 02:27:53PM -0800, Richard Henderson wrote:
> Actually, the custom entIF isn't that much work. What about this?
Hmm, I like it.
However, if I didn't missed something again, some corrections are required.
First, in opDEC_check_entIF r16 should be stored on the stack frame
(PAL_rti will restore it). Also, if SRM is broken and doesn't update pc,
we'll jump back to cvttq/svm. So I think we need something like this:
opDEC_check_entIF: \n\
ldq $16,8($sp) \n\
+ stq $16,24($sp) \n\
+ addq $16,4,$16 \n\
+ stq $16,8($sp) \n\
call_pal 63 /* PAL_rti */ \n\
and
"cvttq/svm $f31, $f31\n\t"
+ "nop\n\t"
"subq $16, %0, %0"
No?
Ivan.