kvm: x86: Add support for x86 INIT and STARTUP handling

This changeset adds support for INIT and STARTUP IPI handling. We
currently handle both of these interrupts in gem5 and transfer the
state to KVM. Since we do not have a BIOS loaded, we pretend that the
INIT interrupt suspends the CPU after reset.

--HG--
extra : rebase_source : 7f3b25f3801d68f668b6cd91eaf50d6f48ee2a6a
This commit is contained in:
Andreas Sandberg 2014-03-16 17:28:23 +01:00
parent 32bf74cb8e
commit f791e7b313
4 changed files with 50 additions and 6 deletions

View file

@ -281,6 +281,12 @@ class Interrupts : public BasicPioDevice, IntDevice
* @return true if there are interrupts pending. * @return true if there are interrupts pending.
*/ */
bool checkInterruptsRaw() const; 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); Fault getInterrupt(ThreadContext *tc);
void updateIntrInfo(ThreadContext *tc); void updateIntrInfo(ThreadContext *tc);

View file

@ -543,6 +543,12 @@ BaseKvmCPU::tick()
delay = kvmRun(ticksToExecute); 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 // 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 // context from KVM if we want to access it. Flag the KVM state as
// dirty with respect to the cached thread context. // dirty with respect to the cached thread context.

View file

@ -241,6 +241,11 @@ class BaseKvmCPU : public BaseCPU
* make sure that the KVM state is synchronized and that the TC is * make sure that the KVM state is synchronized and that the TC is
* invalidated after entering KVM. * 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 * @param ticks Number of ticks to execute, set to 0 to exit
* immediately after finishing pending operations. * immediately after finishing pending operations.
* @return Number of ticks executed (see note) * @return Number of ticks executed (see note)

View file

@ -1137,7 +1137,27 @@ X86KvmCPU::deliverInterrupts()
interrupts->updateIntrInfo(tc); interrupts->updateIntrInfo(tc);
X86Interrupt *x86int(dynamic_cast<X86Interrupt *>(fault.get())); 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; struct kvm_interrupt kvm_int;
kvm_int.irq = x86int->getVector(); kvm_int.irq = x86int->getVector();
@ -1145,9 +1165,6 @@ X86KvmCPU::deliverInterrupts()
fault->name(), kvm_int.irq); fault->name(), kvm_int.irq);
kvmInterrupt(kvm_int); kvmInterrupt(kvm_int);
} else if (dynamic_cast<NonMaskableInterrupt *>(fault.get())) {
DPRINTF(KvmInt, "Delivering NMI\n");
kvmNonMaskableInterrupt();
} else { } else {
panic("KVM: Unknown interrupt type\n"); panic("KVM: Unknown interrupt type\n");
} }
@ -1160,7 +1177,12 @@ X86KvmCPU::kvmRun(Tick ticks)
struct kvm_run &kvm_run(*getKvmRunState()); struct kvm_run &kvm_run(*getKvmRunState());
if (interrupts->checkInterruptsRaw()) { 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 // KVM claims that it is ready for an interrupt. It might
// be lying if we just updated rflags and disabled // be lying if we just updated rflags and disabled
// interrupts (e.g., by doing a CPU handover). Let's sync // interrupts (e.g., by doing a CPU handover). Let's sync
@ -1187,6 +1209,11 @@ X86KvmCPU::kvmRun(Tick ticks)
kvm_run.request_interrupt_window = 0; kvm_run.request_interrupt_window = 0;
} }
// 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); return kvmRunWrapper(ticks);
} }