diff options
-rw-r--r-- | cpu-exec.c | 48 | ||||
-rw-r--r-- | include/sysemu/replay.h | 17 | ||||
-rw-r--r-- | replay/replay-internal.h | 4 | ||||
-rw-r--r-- | replay/replay.c | 67 | ||||
-rw-r--r-- | stubs/replay-user.c | 20 |
5 files changed, 146 insertions, 10 deletions
diff --git a/cpu-exec.c b/cpu-exec.c index a1e3d58692..a57b388623 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -30,6 +30,7 @@ #if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY) #include "hw/i386/apic.h" #endif +#include "sysemu/replay.h" /* -icount align implementation. */ @@ -346,21 +347,25 @@ int cpu_exec(CPUState *cpu) uintptr_t next_tb; SyncClocks sc; + /* replay_interrupt may need current_cpu */ + current_cpu = cpu; + if (cpu->halted) { #if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY) - if (cpu->interrupt_request & CPU_INTERRUPT_POLL) { + if ((cpu->interrupt_request & CPU_INTERRUPT_POLL) + && replay_interrupt()) { apic_poll_irq(x86_cpu->apic_state); cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL); } #endif if (!cpu_has_work(cpu)) { + current_cpu = NULL; return EXCP_HALTED; } cpu->halted = 0; } - current_cpu = cpu; atomic_mb_set(&tcg_current_cpu, cpu); rcu_read_lock(); @@ -402,10 +407,22 @@ int cpu_exec(CPUState *cpu) cpu->exception_index = -1; break; #else - cc->do_interrupt(cpu); - cpu->exception_index = -1; + if (replay_exception()) { + cc->do_interrupt(cpu); + cpu->exception_index = -1; + } else if (!replay_has_interrupt()) { + /* give a chance to iothread in replay mode */ + ret = EXCP_INTERRUPT; + break; + } #endif } + } else if (replay_has_exception() + && cpu->icount_decr.u16.low + cpu->icount_extra == 0) { + /* try to cause an exception pending in the log */ + cpu_exec_nocache(cpu, 1, tb_find_fast(cpu), true); + ret = -1; + break; } next_tb = 0; /* force lookup of first TB */ @@ -421,30 +438,40 @@ int cpu_exec(CPUState *cpu) cpu->exception_index = EXCP_DEBUG; cpu_loop_exit(cpu); } - if (interrupt_request & CPU_INTERRUPT_HALT) { + if (replay_mode == REPLAY_MODE_PLAY + && !replay_has_interrupt()) { + /* Do nothing */ + } else if (interrupt_request & CPU_INTERRUPT_HALT) { + replay_interrupt(); cpu->interrupt_request &= ~CPU_INTERRUPT_HALT; cpu->halted = 1; cpu->exception_index = EXCP_HLT; cpu_loop_exit(cpu); } #if defined(TARGET_I386) - if (interrupt_request & CPU_INTERRUPT_INIT) { + else if (interrupt_request & CPU_INTERRUPT_INIT) { + replay_interrupt(); cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0); do_cpu_init(x86_cpu); cpu->exception_index = EXCP_HALTED; cpu_loop_exit(cpu); } #else - if (interrupt_request & CPU_INTERRUPT_RESET) { + else if (interrupt_request & CPU_INTERRUPT_RESET) { + replay_interrupt(); cpu_reset(cpu); + cpu_loop_exit(cpu); } #endif /* The target hook has 3 exit conditions: False when the interrupt isn't processed, True when it is, and we should restart on a new TB, and via longjmp via cpu_loop_exit. */ - if (cc->cpu_exec_interrupt(cpu, interrupt_request)) { - next_tb = 0; + else { + replay_interrupt(); + if (cc->cpu_exec_interrupt(cpu, interrupt_request)) { + next_tb = 0; + } } /* Don't use the cached interrupt_request value, do_interrupt may have updated the EXITTB flag. */ @@ -455,7 +482,8 @@ int cpu_exec(CPUState *cpu) next_tb = 0; } } - if (unlikely(cpu->exit_request)) { + if (unlikely(cpu->exit_request + || replay_has_interrupt())) { cpu->exit_request = 0; cpu->exception_index = EXCP_INTERRUPT; cpu_loop_exit(cpu); diff --git a/include/sysemu/replay.h b/include/sysemu/replay.h index d19715fde0..8915523eaf 100644 --- a/include/sysemu/replay.h +++ b/include/sysemu/replay.h @@ -27,4 +27,21 @@ int replay_get_instructions(void); /*! Updates instructions counter in replay mode. */ void replay_account_executed_instructions(void); +/* Interrupts and exceptions */ + +/*! Called by exception handler to write or read + exception processing events. */ +bool replay_exception(void); +/*! Used to determine that exception is pending. + Does not proceed to the next event in the log. */ +bool replay_has_exception(void); +/*! Called by interrupt handlers to write or read + interrupt processing events. + \return true if interrupt should be processed */ +bool replay_interrupt(void); +/*! Tries to read interrupt event from the file. + Returns true, when interrupt request is pending */ +bool replay_has_interrupt(void); + + #endif diff --git a/replay/replay-internal.h b/replay/replay-internal.h index ff4fabc326..5ff1c14287 100644 --- a/replay/replay-internal.h +++ b/replay/replay-internal.h @@ -17,6 +17,10 @@ enum ReplayEvents { /* for instruction event */ EVENT_INSTRUCTION, + /* for software interrupt */ + EVENT_INTERRUPT, + /* for emulated exceptions */ + EVENT_EXCEPTION, EVENT_COUNT }; diff --git a/replay/replay.c b/replay/replay.c index b2c67501a5..b4fc64acee 100644 --- a/replay/replay.c +++ b/replay/replay.c @@ -79,3 +79,70 @@ void replay_account_executed_instructions(void) replay_mutex_unlock(); } } + +bool replay_exception(void) +{ + if (replay_mode == REPLAY_MODE_RECORD) { + replay_save_instructions(); + replay_mutex_lock(); + replay_put_event(EVENT_EXCEPTION); + replay_mutex_unlock(); + return true; + } else if (replay_mode == REPLAY_MODE_PLAY) { + bool res = replay_has_exception(); + if (res) { + replay_mutex_lock(); + replay_finish_event(); + replay_mutex_unlock(); + } + return res; + } + + return true; +} + +bool replay_has_exception(void) +{ + bool res = false; + if (replay_mode == REPLAY_MODE_PLAY) { + replay_account_executed_instructions(); + replay_mutex_lock(); + res = replay_next_event_is(EVENT_EXCEPTION); + replay_mutex_unlock(); + } + + return res; +} + +bool replay_interrupt(void) +{ + if (replay_mode == REPLAY_MODE_RECORD) { + replay_save_instructions(); + replay_mutex_lock(); + replay_put_event(EVENT_INTERRUPT); + replay_mutex_unlock(); + return true; + } else if (replay_mode == REPLAY_MODE_PLAY) { + bool res = replay_has_interrupt(); + if (res) { + replay_mutex_lock(); + replay_finish_event(); + replay_mutex_unlock(); + } + return res; + } + + return true; +} + +bool replay_has_interrupt(void) +{ + bool res = false; + if (replay_mode == REPLAY_MODE_PLAY) { + replay_account_executed_instructions(); + replay_mutex_lock(); + res = replay_next_event_is(EVENT_INTERRUPT); + replay_mutex_unlock(); + } + return res; +} diff --git a/stubs/replay-user.c b/stubs/replay-user.c index 0c90bfc0e4..cf330721c8 100644 --- a/stubs/replay-user.c +++ b/stubs/replay-user.c @@ -10,3 +10,23 @@ */ #include "sysemu/replay.h" + +bool replay_exception(void) +{ + return true; +} + +bool replay_has_exception(void) +{ + return false; +} + +bool replay_interrupt(void) +{ + return true; +} + +bool replay_has_interrupt(void) +{ + return false; +} |