#include <csignal>
#include <ostream>
+#include "arch/mmapped_ipr.hh"
#include "arch/utility.hh"
#include "cpu/kvm/base.hh"
#include "debug/Checkpoint.hh"
volatile bool timerOverflowed = false;
-static void
-onTimerOverflow(int signo, siginfo_t *si, void *data)
-{
- timerOverflowed = true;
-}
-
BaseKvmCPU::BaseKvmCPU(BaseKvmCPUParams *params)
: BaseCPU(params),
vm(*params->kvmVM),
_kvmRun(NULL), mmioRing(NULL),
pageSize(sysconf(_SC_PAGE_SIZE)),
tickEvent(*this),
+ activeInstPeriod(0),
perfControlledByTimer(params->usePerfOverflow),
+ hostFreq(params->hostFreq),
hostFactor(params->hostFactor),
drainManager(NULL),
ctrInsts(0)
runTimer.reset(new PerfKvmTimer(hwCycles,
KVM_TIMER_SIGNAL,
params->hostFactor,
- params->clock));
+ params->hostFreq));
else
runTimer.reset(new PosixKvmTimer(KVM_TIMER_SIGNAL, CLOCK_MONOTONIC,
params->hostFactor,
- params->clock));
+ params->hostFreq));
}
BaseKvmCPU::~BaseKvmCPU()
.desc("total number of KVM exits")
;
+ numVMHalfEntries
+ .name(name() + ".numVMHalfEntries")
+ .desc("number of KVM entries to finalize pending operations")
+ ;
+
+ numExitSignal
+ .name(name() + ".numExitSignal")
+ .desc("exits due to signal delivery")
+ ;
+
numMMIO
.name(name() + ".numMMIO")
.desc("number of VM exits due to memory mapped IO")
assert(_status == Idle);
assert(!tickEvent.scheduled());
- numCycles += ticksToCycles(thread->lastActivate - thread->lastSuspend)
- * hostFactor;
+ numCycles += ticksToCycles(thread->lastActivate - thread->lastSuspend);
schedule(tickEvent, clockEdge(delay));
_status = Running;
// We might need to update the KVM state.
syncKvmState();
+ // Setup any pending instruction count breakpoints using
+ // PerfEvent.
+ setupInstStop();
+
DPRINTF(KvmRun, "Entering KVM...\n");
if (drainManager) {
// Force an immediate exit from KVM after completing
// Enter into the RunningService state unless the
// simulation was stopped by a timer.
- if (_kvmRun->exit_reason != KVM_EXIT_INTR)
+ if (_kvmRun->exit_reason != KVM_EXIT_INTR) {
_status = RunningService;
- else
+ } else {
+ ++numExitSignal;
_status = Running;
+ }
+
+ // Service any pending instruction events. The vCPU should
+ // have exited in time for the event using the instruction
+ // counter configured by setupInstStop().
+ comInstEventQueue[0]->serviceEvents(ctrInsts);
+ system->instEventQueue.serviceEvents(system->totalNumInsts);
if (tryDrain())
_status = Idle;
// then immediately exits.
DPRINTF(KvmRun, "KVM: Delivering IO without full guest entry\n");
+ ++numVMHalfEntries;
+
// This signal is always masked while we are executing in gem5
// and gets unmasked temporarily as soon as we enter into
// KVM. See setSignalMask() and setupSignalHandler().
// sure we don't deliver it immediately next time we try to
// enter into KVM.
discardPendingSignal(KVM_TIMER_SIGNAL);
+ discardPendingSignal(KVM_INST_SIGNAL);
const uint64_t hostCyclesExecuted(getHostCycles() - baseCycles);
const uint64_t simCyclesExecuted(hostCyclesExecuted * hostFactor);
Tick
BaseKvmCPU::doMMIOAccess(Addr paddr, void *data, int size, bool write)
{
+ ThreadContext *tc(thread->getTC());
+ syncThreadContext();
+
mmio_req.setPhys(paddr, size, Request::UNCACHEABLE, dataMasterId());
+ // Some architectures do need to massage physical addresses a bit
+ // before they are inserted into the memory system. This enables
+ // APIC accesses on x86 and m5ops where supported through a MMIO
+ // interface.
+ BaseTLB::Mode tlb_mode(write ? BaseTLB::Write : BaseTLB::Read);
+ Fault fault(tc->getDTBPtr()->finalizePhysical(&mmio_req, tc, tlb_mode));
+ if (fault != NoFault)
+ warn("Finalization of MMIO address failed: %s\n", fault->name());
+
const MemCmd cmd(write ? MemCmd::WriteReq : MemCmd::ReadReq);
Packet pkt(&mmio_req, cmd);
pkt.dataStatic(data);
- return dataPort.sendAtomic(&pkt);
+
+ if (mmio_req.isMmappedIpr()) {
+ const Cycles ipr_delay(write ?
+ TheISA::handleIprWrite(tc, &pkt) :
+ TheISA::handleIprRead(tc, &pkt));
+ return clockEdge(ipr_delay);
+ } else {
+ return dataPort.sendAtomic(&pkt);
+ }
}
void
return ticks;
}
+/**
+ * Cycle timer overflow when running in KVM. Forces the KVM syscall to
+ * exit with EINTR and allows us to run the event queue.
+ */
+static void
+onTimerOverflow(int signo, siginfo_t *si, void *data)
+{
+ timerOverflowed = true;
+}
+
+/**
+ * Instruction counter overflow when running in KVM. Forces the KVM
+ * syscall to exit with EINTR and allows us to handle instruction
+ * count events.
+ */
+static void
+onInstEvent(int signo, siginfo_t *si, void *data)
+{
+}
+
void
BaseKvmCPU::setupSignalHandler()
{
sa.sa_sigaction = onTimerOverflow;
sa.sa_flags = SA_SIGINFO | SA_RESTART;
if (sigaction(KVM_TIMER_SIGNAL, &sa, NULL) == -1)
- panic("KVM: Failed to setup vCPU signal handler\n");
+ panic("KVM: Failed to setup vCPU timer signal handler\n");
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = onInstEvent;
+ sa.sa_flags = SA_SIGINFO | SA_RESTART;
+ if (sigaction(KVM_INST_SIGNAL, &sa, NULL) == -1)
+ panic("KVM: Failed to setup vCPU instruction signal handler\n");
sigset_t sigset;
if (sigprocmask(SIG_BLOCK, NULL, &sigset) == -1)
// requests. See kvmRun().
setSignalMask(&sigset);
- // Mask the KVM_TIMER_SIGNAL so it isn't delivered unless we're
+ // Mask our control signals so they aren't delivered unless we're
// actually executing inside KVM.
sigaddset(&sigset, KVM_TIMER_SIGNAL);
+ sigaddset(&sigset, KVM_INST_SIGNAL);
if (sigprocmask(SIG_SETMASK, &sigset, NULL) == -1)
- panic("KVM: Failed mask the KVM timer signal\n");
+ panic("KVM: Failed mask the KVM control signals\n");
}
bool
hwCycles.attach(cfgCycles,
0); // TID (0 => currentThread)
- DPRINTF(Kvm, "Attaching instruction counter...\n");
- PerfKvmCounterConfig cfgInstructions(PERF_TYPE_HARDWARE,
- PERF_COUNT_HW_INSTRUCTIONS);
- hwInstructions.attach(cfgInstructions,
- 0, // TID (0 => currentThread)
- hwCycles);
+ setupInstCounter();
}
bool
errno);
}
}
+
+void
+BaseKvmCPU::setupInstStop()
+{
+ if (comInstEventQueue[0]->empty()) {
+ setupInstCounter(0);
+ } else {
+ const uint64_t next(comInstEventQueue[0]->nextTick());
+
+ assert(next > ctrInsts);
+ setupInstCounter(next - ctrInsts);
+ }
+}
+
+void
+BaseKvmCPU::setupInstCounter(uint64_t period)
+{
+ // No need to do anything if we aren't attaching for the first
+ // time or the period isn't changing.
+ if (period == activeInstPeriod && hwInstructions.attached())
+ return;
+
+ PerfKvmCounterConfig cfgInstructions(PERF_TYPE_HARDWARE,
+ PERF_COUNT_HW_INSTRUCTIONS);
+
+ if (period) {
+ // Setup a sampling counter if that has been requested.
+ cfgInstructions.wakeupEvents(1)
+ .samplePeriod(period);
+ }
+
+ // We need to detach and re-attach the counter to reliably change
+ // sampling settings. See PerfKvmCounter::period() for details.
+ if (hwInstructions.attached())
+ hwInstructions.detach();
+ assert(hwCycles.attached());
+ hwInstructions.attach(cfgInstructions,
+ 0, // TID (0 => currentThread)
+ hwCycles);
+
+ if (period)
+ hwInstructions.enableSignals(KVM_INST_SIGNAL);
+
+ activeInstPeriod = period;
+}