diff --git a/src/arch/x86/interrupts.hh b/src/arch/x86/interrupts.hh index 8997d7402b..dabee54416 100644 --- a/src/arch/x86/interrupts.hh +++ b/src/arch/x86/interrupts.hh @@ -281,6 +281,12 @@ class Interrupts : public BasicPioDevice, IntDevice * @return true if there are interrupts pending. */ bool checkInterruptsRaw() const; + /** + * Check if there are pending unmaskable interrupts. + * + * @return true there are unmaskable interrupts pending. + */ + bool hasPendingUnmaskable() const { return pendingUnmaskableInt; } Fault getInterrupt(ThreadContext *tc); void updateIntrInfo(ThreadContext *tc); diff --git a/src/cpu/kvm/base.cc b/src/cpu/kvm/base.cc index 26ffe37a54..1149a37043 100644 --- a/src/cpu/kvm/base.cc +++ b/src/cpu/kvm/base.cc @@ -543,6 +543,12 @@ BaseKvmCPU::tick() delay = kvmRun(ticksToExecute); } + // The CPU might have been suspended before entering into + // KVM. Assume that the CPU was suspended /before/ entering + // into KVM and skip the exit handling. + if (_status == Idle) + break; + // Entering into KVM implies that we'll have to reload the thread // context from KVM if we want to access it. Flag the KVM state as // dirty with respect to the cached thread context. diff --git a/src/cpu/kvm/base.hh b/src/cpu/kvm/base.hh index 5a0b80b15a..8191d9d92e 100644 --- a/src/cpu/kvm/base.hh +++ b/src/cpu/kvm/base.hh @@ -241,6 +241,11 @@ class BaseKvmCPU : public BaseCPU * 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 must not 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) diff --git a/src/cpu/kvm/x86_cpu.cc b/src/cpu/kvm/x86_cpu.cc index b79207fab3..3313c8db06 100644 --- a/src/cpu/kvm/x86_cpu.cc +++ b/src/cpu/kvm/x86_cpu.cc @@ -1137,7 +1137,27 @@ X86KvmCPU::deliverInterrupts() interrupts->updateIntrInfo(tc); X86Interrupt *x86int(dynamic_cast(fault.get())); - if (x86int) { + if (dynamic_cast(fault.get())) { + DPRINTF(KvmInt, "Delivering NMI\n"); + kvmNonMaskableInterrupt(); + } else if (dynamic_cast(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(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(); @@ -1145,9 +1165,6 @@ X86KvmCPU::deliverInterrupts() fault->name(), kvm_int.irq); kvmInterrupt(kvm_int); - } else if (dynamic_cast(fault.get())) { - DPRINTF(KvmInt, "Delivering NMI\n"); - kvmNonMaskableInterrupt(); } else { panic("KVM: Unknown interrupt type\n"); } @@ -1160,7 +1177,12 @@ X86KvmCPU::kvmRun(Tick ticks) 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 @@ -1187,7 +1209,12 @@ X86KvmCPU::kvmRun(Tick ticks) 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