summaryrefslogtreecommitdiff
path: root/src/cpu/kvm
diff options
context:
space:
mode:
Diffstat (limited to 'src/cpu/kvm')
-rw-r--r--src/cpu/kvm/base.cc6
-rw-r--r--src/cpu/kvm/base.hh5
-rw-r--r--src/cpu/kvm/x86_cpu.cc39
3 files changed, 44 insertions, 6 deletions
diff --git a/src/cpu/kvm/base.cc b/src/cpu/kvm/base.cc
index 26ffe37a5..1149a3704 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 5a0b80b15..8191d9d92 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 <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)
diff --git a/src/cpu/kvm/x86_cpu.cc b/src/cpu/kvm/x86_cpu.cc
index b79207fab..3313c8db0 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<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();
@@ -1145,9 +1165,6 @@ X86KvmCPU::deliverInterrupts()
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");
}
@@ -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