diff options
author | ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162> | 2007-03-31 20:28:52 +0000 |
---|---|---|
committer | ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162> | 2007-03-31 20:28:52 +0000 |
commit | 678dde1323f864a46730c303223b40c4571c23bb (patch) | |
tree | 26bec94c0725edc7f351d815a697d4ff85750837 /target-i386/helper.c | |
parent | a80274c31bd94f7a345934d2544075a6d183ebac (diff) | |
download | qemu-678dde1323f864a46730c303223b40c4571c23bb.zip |
Generate double and triple faults, by Bernhard Kauer.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2563 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'target-i386/helper.c')
-rw-r--r-- | target-i386/helper.c | 39 |
1 files changed, 39 insertions, 0 deletions
diff --git a/target-i386/helper.c b/target-i386/helper.c index 1d62f6b2e3..70370fca29 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -1193,6 +1193,40 @@ void do_interrupt(int intno, int is_int, int error_code, } /* + * Check nested exceptions and change to double or triple fault if + * needed. It should only be called, if this is not an interrupt. + * Returns the new exception number. + */ +int check_exception(int intno, int *error_code) +{ + char first_contributory = env->old_exception == 0 || + (env->old_exception >= 10 && + env->old_exception <= 13); + char second_contributory = intno == 0 || + (intno >= 10 && intno <= 13); + + if (loglevel & CPU_LOG_INT) + fprintf(logfile, "check_exception old: %x new %x\n", + env->old_exception, intno); + + if (env->old_exception == EXCP08_DBLE) + cpu_abort(env, "triple fault"); + + if ((first_contributory && second_contributory) + || (env->old_exception == EXCP0E_PAGE && + (second_contributory || (intno == EXCP0E_PAGE)))) { + intno = EXCP08_DBLE; + *error_code = 0; + } + + if (second_contributory || (intno == EXCP0E_PAGE) || + (intno == EXCP08_DBLE)) + env->old_exception = intno; + + return intno; +} + +/* * Signal an interruption. It is executed in the main CPU loop. * is_int is TRUE if coming from the int instruction. next_eip is the * EIP value AFTER the interrupt instruction. It is only relevant if @@ -1201,6 +1235,9 @@ void do_interrupt(int intno, int is_int, int error_code, void raise_interrupt(int intno, int is_int, int error_code, int next_eip_addend) { + if (!is_int) + intno = check_exception(intno, &error_code); + env->exception_index = intno; env->error_code = error_code; env->exception_is_int = is_int; @@ -1211,6 +1248,8 @@ void raise_interrupt(int intno, int is_int, int error_code, /* same as raise_exception_err, but do not restore global registers */ static void raise_exception_err_norestore(int exception_index, int error_code) { + exception_index = check_exception(exception_index, &error_code); + env->exception_index = exception_index; env->error_code = error_code; env->exception_is_int = 0; |