2001-11-20 07:24:29

by Richard Henderson

[permalink] [raw]
Subject: [alpha] cleanup opDEC workaround

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"),
- &regs, type, 0);
- }
-
switch (type) {
- case 0: /* breakpoint */
+ case 0: /* breakpoint */
+ die_if_kernel("Breakpoint fault", &regs, 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", &regs, type, 0);
send_sig(SIGTRAP, current, 1);
return;

- case 2: /* gentrap */
+ case 2: /* gentrap */
+ die_if_kernel("Divide by zero fault", &regs, 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(&current->thread);
return;

- case 5: /* illoc */
- default: /* unexpected instruction-fault type */
- ;
+ case 5: /* illoc */
+ default: /* unexpected instruction-fault type */
+ break;
}
+
+ die_if_kernel("Instruction fault", &regs, 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();
- }
}


2001-11-20 10:32:51

by Ivan Kokshaysky

[permalink] [raw]
Subject: Re: [alpha] cleanup opDEC workaround

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.

2001-11-20 17:08:57

by Richard Henderson

[permalink] [raw]
Subject: Re: [alpha] cleanup opDEC workaround

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(&current->thread);
+ return;
+
case 5: /* illoc */
default: /* unexpected instruction-fault type */
;

2001-11-20 17:52:01

by Ivan Kokshaysky

[permalink] [raw]
Subject: Re: [alpha] cleanup opDEC workaround

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.

2001-11-20 18:48:33

by Richard Henderson

[permalink] [raw]
Subject: Re: [alpha] cleanup opDEC workaround

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~

2001-11-21 12:16:47

by Ivan Kokshaysky

[permalink] [raw]
Subject: Re: [alpha] cleanup opDEC workaround

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.

2001-11-21 22:28:33

by Richard Henderson

[permalink] [raw]
Subject: Re: [alpha] cleanup opDEC workaround

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"),
- &regs, type, 0);
- }
-
switch (type) {
- case 0: /* breakpoint */
+ case 0: /* breakpoint */
+ die_if_kernel("Breakpoint fault", &regs, 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", &regs, type, 0);
send_sig(SIGTRAP, current, 1);
return;

- case 2: /* gentrap */
+ case 2: /* gentrap */
+ die_if_kernel("Divide by zero fault", &regs, 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(&current->thread);
return;

- case 5: /* illoc */
- default: /* unexpected instruction-fault type */
- ;
+ case 5: /* illoc */
+ default: /* unexpected instruction-fault type */
+ break;
}
+
+ die_if_kernel("Instruction fault", &regs, 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();
- }
}

2001-11-22 10:56:31

by Ivan Kokshaysky

[permalink] [raw]
Subject: Re: [alpha] cleanup opDEC workaround

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.