* make sure that the KVM state is synchronized and that the TC is
* invalidated after entering KVM.
*
+ * @note This method does not normally cause any state
+ * transitions. However, if it may suspend the CPU by suspending
+ * the thread, which leads to a transition to the Idle state. In
+ * such a case, kvm <i>must not</i> be entered.
+ *
* @param ticks Number of ticks to execute, set to 0 to exit
* immediately after finishing pending operations.
* @return Number of ticks executed (see note)
interrupts->updateIntrInfo(tc);
X86Interrupt *x86int(dynamic_cast<X86Interrupt *>(fault.get()));
- if (x86int) {
+ if (dynamic_cast<NonMaskableInterrupt *>(fault.get())) {
+ DPRINTF(KvmInt, "Delivering NMI\n");
+ kvmNonMaskableInterrupt();
+ } else if (dynamic_cast<InitInterrupt *>(fault.get())) {
+ DPRINTF(KvmInt, "INIT interrupt\n");
+ fault.get()->invoke(tc);
+ // Delay the kvm state update since we won't enter KVM on this
+ // tick.
+ threadContextDirty = true;
+ // HACK: gem5 doesn't actually have any BIOS code, which means
+ // that we need to halt the thread and wait for a startup
+ // interrupt before restarting the thread. The simulated CPUs
+ // use the same kind of hack using a microcode routine.
+ thread->suspend();
+ } else if (dynamic_cast<StartupInterrupt *>(fault.get())) {
+ DPRINTF(KvmInt, "STARTUP interrupt\n");
+ fault.get()->invoke(tc);
+ // The kvm state is assumed to have been updated when entering
+ // kvmRun(), so we need to update manually it here.
+ updateKvmState();
+ } else if (x86int) {
struct kvm_interrupt kvm_int;
kvm_int.irq = x86int->getVector();
fault->name(), kvm_int.irq);
kvmInterrupt(kvm_int);
- } else if (dynamic_cast<NonMaskableInterrupt *>(fault.get())) {
- DPRINTF(KvmInt, "Delivering NMI\n");
- kvmNonMaskableInterrupt();
} else {
panic("KVM: Unknown interrupt type\n");
}
struct kvm_run &kvm_run(*getKvmRunState());
if (interrupts->checkInterruptsRaw()) {
- if (kvm_run.ready_for_interrupt_injection) {
+ if (interrupts->hasPendingUnmaskable()) {
+ DPRINTF(KvmInt,
+ "Delivering unmaskable interrupt.\n");
+ syncThreadContext();
+ deliverInterrupts();
+ } else if (kvm_run.ready_for_interrupt_injection) {
// KVM claims that it is ready for an interrupt. It might
// be lying if we just updated rflags and disabled
// interrupts (e.g., by doing a CPU handover). Let's sync
kvm_run.request_interrupt_window = 0;
}
- return kvmRunWrapper(ticks);
+ // The CPU might have been suspended as a result of the INIT
+ // interrupt delivery hack. In that case, don't enter into KVM.
+ if (_status == Idle)
+ return 0;
+ else
+ return kvmRunWrapper(ticks);
}
Tick