Return to BSD News archive
Newsgroups: comp.unix.bsd
Path: sserve!manuel.anu.edu.au!munnari.oz.au!metro!ipso!runxtsa!bde
From: bde@runx.oz.au (Bruce Evans)
Subject: Re: [386bsd] How To Nuke 386bsd!!!
Message-ID: <1992Dec15.210710.28798@runx.oz.au>
Organization: RUNX Un*x Timeshare. Sydney, Australia.
References: <ByntK9.1D6@news.chalmers.se> <1992Dec7.000113.20304@mel.dit.csiro.au> <1992Dec7.034604.14841@netcom.com>
Date: Tue, 15 Dec 92 21:07:10 GMT
Lines: 182
In article <1992Dec7.034604.14841@netcom.com> hasty@netcom.com (Amancio Hasty Jr) writes:
>[...]
>The problem maybe related to the way that you motherboard handles
>floating point exception. Try a simple program that does a floating
>point divide by 0.0. If the system re-boots you got a bad motherboard.
There are so many bugs in the kernel and libraries involving floating
point exceptions that it's hard to tell if the motherboard is bad. I
seem to have a "bad" 486 motherboard (386BSD-0.1 panics) but I have
rewritten the exception handler so that there are no problems.
Here is a test program that does a lot of floating point divides by 0.0
and panics 386BSD-0.1 on a bad (any?) motherboard fairly quickly. Compile
it with -DMAX_FAILURES=16 to limit the output. Comment out any test that
always fails to emphasize the other tests.
I am interested in how non-386BSD 386-486 systems handle this test. It
requires gcc to compile so I haven't been able to run it on any, but
previous tests suggest that failure of all the "no-wait" tests except
the one for fnclex is typical, and when the fnclex test doesn't fail all
the time, it fails intermittently.
---
#include <signal.h>
#include <stdio.h>
#define TEST(x, y) ( ++tests, setup(), ({ asm(x); 0; }), check(x, y) )
#define CW_ZM (1 << 2) /* divide by zero mask */
#define SW_BUSY (1 << 15) /* FPU busy */
#define SW_ES (1 << 7) /* exception summary */
#define SW_ZE (1 << 2) /* divide by zero (pending) exception seen */
static double double_in_mem;
static unsigned short fp_cw;
static unsigned fp_env[7];
static unsigned fp_state[7 + 8 * 10 / sizeof(unsigned)];
static unsigned short fp_sw;
static unsigned failures;
static unsigned long tests;
static volatile /* sig_atomic_t */ int sigfpe_handled;
static volatile /* sig_atomic_t */ int sigint_handled;
static void delay(void);
static int check(char *insn, int sigfpe_expected);
static void setup(void);
static void sigfpe_handler(int sig_num);
static void sigint_handler(int sig_num);
static int check(char *insn, int sigfpe_expected)
{
if (sigfpe_handled)
{
signal(SIGFPE, sigfpe_handler);
sigfpe_handled = 0;
if (sigfpe_expected)
return 1;
}
else if (!sigfpe_expected)
return 1;
++failures;
fprintf(stderr, "T %lu F %u: %s SIGFPE for `%s'\n",
tests, failures, sigfpe_expected ? "no" : " ", insn);
return 0;
}
static void delay(void)
{
volatile unsigned countdown;
for (countdown = 100; countdown != 0; --countdown)
;
}
int main(argc, argv)
int argc;
char **argv;
{
signal(SIGINT, sigint_handler);
#ifdef MAX_FAILURES
while (failures < MAX_FAILURES && !sigint_handled)
#else
while (!sigint_handled)
#endif
{
/*
* Cause a divide by zero error. This should not trigger an exception.
* The next no-wait FP instruction should trigger the exception.
*/
if (TEST("fldz; fld1; fdiv %st,%st(1)", 0))
{
/*
* The wait instruction should always trigger a pending exception.
*
* One way for this to fail is if the kernel uses CR0_EM instead
* of CR0_TS | CR0_MP to handle FP context switching. This fails
* to trap fwaits immeditatly after an FP context switch. It is
* especially bad when FP is being emulated. Then all fwaits are
* ignored!
*/
TEST("fldz; fld1; fdiv %st,%st(1); call _delay; fwait", 1);
/*
* No-wait instructions should never trigger a pending exception.
*
* On my 486 system, they are all broken when the IRQ13 FP
* exception reporting method is used. On at least one 386 system,
* fnclex usually works but some of the others are broken, and
* fnclex fails after a context switch, presumably because frstor
* fails in the kernel.
*/
TEST("fldz; fld1; fdiv %st,%st(1); fninit", 0);
TEST("fldz; fld1; fdiv %st,%st(1); fnstcw _fp_cw", 0);
TEST("fldz; fld1; fdiv %st,%st(1); fnstsw _fp_sw", 0);
TEST("fldz; fld1; fdiv %st,%st(1); fnclex", 0);
TEST("fldz; fld1; fdiv %st,%st(1); fnstenv _fp_env", 0);
TEST("fldz; fld1; fdiv %st,%st(1); fnsave _fp_state", 0);
}
/*
* fldenv and frstor of an error state should not trigger an exception,
* and they should not lose the pending exception. Fake the pending
* exception so that these tests can be done even if the tests for
* fnstenv and fnsave of the pending exception failed.
*/
setup();
asm("fnstenv _fp_env"); /* an almost clean env */
fp_env[1] |= SW_BUSY | SW_ES | SW_ZE; /* fake excepttion */
++tests;
asm("fldenv _fp_env");
if (check("fldenv of pending exception", 0))
{
delay();
asm("fwait");
++tests;
check("fwait after fldenv of pending exception", 1);
}
setup();
asm("fnsave _fp_state"); /* an almost clean state */
fp_state[1] |= SW_BUSY | SW_ES | SW_ZE; /* fake excepttion */
++tests;
asm("frstor _fp_state");
if (check("frstor of pending exception", 0))
{
delay();
asm("fwait");
++tests;
check("fwait after frstor of pending exception", 1);
}
/*
* fstpl to memory when the FP stack is empty sometimes causes an
* IRQ13 a little after the intstruction. When the fstpl is traced,
* the exception appears to come from the trace trap handler! frstor
* of a pending error may also cause an IRQ13 after the instruction.
*/
TEST("fstpl _double_in_mem", 0);
}
fprintf(stderr, "%lu tests, %u failures\n", tests, failures);
return failures ? 1 : 0;
}
static void setup(void)
{
asm("fwait; fninit; fnstcw _fp_cw");
fp_cw &= ~CW_ZM;
asm("fldcw _fp_cw");
signal(SIGFPE, sigfpe_handler);
sigfpe_handled = 0;
}
static void sigfpe_handler(int sig_num)
{
sigfpe_handled = 1;
}
static void sigint_handler(int sig_num)
{
sigint_handled = 1;
}
---
--
Bruce Evans (bde@runx.oz.au)